1 /*
  2  * Copyright (c) 2022, 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  * @bug 8194743
 26  * @summary Test valid placements of super()/this() in constructors
 27  * @enablePreview
 28  * @ignore fails at execution time because of Optional
 29  */
 30 
 31 import java.util.concurrent.atomic.AtomicReference;
 32 
 33 public class SuperInitGood {
 34 
 35     SuperInitGood(Object obj) {
 36     }
 37 
 38     SuperInitGood(int x) {
 39     }
 40 
 41     // Default constructor provided by compiler
 42     static class Test0 {
 43     }
 44 
 45     // No explicit calls to this()/super()
 46     static class Test1 {
 47         Test1() {
 48         }
 49         Test1(int a) {
 50             this.hashCode();
 51         }
 52     }
 53 
 54     // Explicit calls to this()/super()
 55     static class Test2<T> {
 56         static int i;
 57         Test2() {
 58             this(0);
 59         }
 60         Test2(int i) {
 61             Test2.i = i;
 62             super();
 63         }
 64         Test2(T obj) {
 65             this(java.util.Objects.hashCode(obj));
 66         }
 67         public T get() {
 68             return null;
 69         }
 70     }
 71 
 72     // Explicit this()/super() with stuff in front
 73     static class Test3 {
 74         int x;
 75         final int y;
 76         final int z;
 77 
 78         Test3() {
 79             new Object().hashCode();
 80             new Object().hashCode();
 81             super();
 82             this.x = new Object().hashCode();
 83             this.y = new Object().hashCode() % 17;
 84             this.z = this.x + this.y;
 85         }
 86     }
 87 
 88     // Reference within constructor to outer class that's also my superclass
 89     class Test5 extends SuperInitGood {
 90         Test5(Object obj) {
 91             if (obj == null)
 92                 throw new IllegalArgumentException();
 93             super(SuperInitGood.this);      // NOT a 'this' reference
 94         }
 95     }
 96 
 97     // Initialization blocks
 98     class Test6 {
 99         final long startTime;
100         final int x;
101         {
102             this.x = 12;
103         }
104         Test6() {
105             long now = System.nanoTime();
106             long then = now + 1000000L;
107             while (System.nanoTime() < then) {
108                 try {
109                     Thread.sleep(1);
110                 } catch (InterruptedException e) {
111                     Thread.currentThread().interrupt();
112                     break;
113                 }
114             }
115             super();
116             this.startTime = now;
117         }
118     }
119 
120     // Mix up inner classes, proxies, and super() calls
121     // Copied mostly from UnverifiableInitForNestedLocalClassTest.java
122     public static void test7(final String arg) {
123         final String inlined = " inlined ";
124         class LocalClass {
125             String m() {
126                 return "LocalClass " + arg + inlined;
127             }
128 
129             class SubClass extends LocalClass {
130                 @Override
131                 String m() {
132                     return "SubClass " + arg + inlined;
133                 }
134             }
135 
136             class SubSubClass extends SubClass {
137                 @Override
138                 String m() {
139                     return "SubSubClass " + arg + inlined;
140                 }
141             }
142 
143             class AnotherLocal {
144                 class AnotherSub extends LocalClass {
145                     AnotherSub() {
146                     }
147                     AnotherSub(int x) {
148                         this((char)x);
149                     }
150                     AnotherSub(char y) {
151                         super();
152                     }
153                     @Override
154                     String m() {
155                         return "AnotherSub " + arg + inlined;
156                     }
157                 }
158             }
159         }
160     }
161 
162     // Anonymous inner class
163     public static void test8() {
164         new Test2<Byte>(null) {
165             @Override
166             public Byte get() {
167                 return (byte)-1;
168             }
169         };
170     }
171 
172     // Qualified super() invocation
173     public static class Test9 extends Test5 {
174 
175         public Test9(SuperInitGood implicit, Object obj) {
176             obj.hashCode();
177             implicit.super(obj);
178         }
179     }
180 
181     // Copied from WhichImplicitThis6
182     public static class Test10 {
183         private int i;
184         public Test10(int i) {}
185         public class Sub extends Test10 {
186             public Sub() {
187                 super(i); // i is not inherited, so it is the enclosing i
188             }
189         }
190     }
191 
192     // Two constructors where only one invokes super()
193     public static class Test11 {
194         public Test11() {
195         }
196         public Test11(int x) {
197             super();
198         }
199     }
200 
201     // Nested version of the previous test
202     public static class Test12 {
203         Test12() {
204             class Sub {
205                 public Sub() {
206                 }
207                 public Sub(int j) {
208                     super();
209                 }
210             }
211         }
212     }
213 
214     // Nested super()'s requiring initialization code appended
215     public static class Test13 extends SuperInitGood {
216         final int x = new Object().hashCode();
217         Test13() {
218             super(new Object() {
219                 public void foo() {
220                     class Bar {
221                         final int y = new Object().hashCode();
222                         Bar() {
223                             super();
224                         }
225                         Bar(int ignored) {
226                         }
227                     }
228                 }
229             });
230         }
231     }
232 
233     // Initializer in initializer block
234     public static class Test14 {
235         final int x;                // initialized in constructor
236         final int y;                // initialized in initialization block
237         final int z = 13;           // initialized with intializer value
238         public Test14() {
239             this(0);
240         }
241         public Test14(boolean z) {
242             this.x = z ? 1 : 0;
243         }
244         public Test14(int x) {
245             super();
246             this.x = x;
247         }
248         {
249             this.y = -1;
250         }
251     }
252 
253     // Qualified super() invocation with superclass instance
254     public static class Test15 {
255 
256         final String name;
257 
258         public Test15(String name) {
259             this.name = name;
260         }
261 
262         public class Test15b extends Test15 {
263 
264             public Test15b(String name) {
265                 super(name);
266             }
267 
268             public String getName() {
269                 return Test15.this.name;
270             }
271         }
272     }
273 
274     public static class Test15c extends Test15.Test15b {
275         public Test15c(Test15 a, String name) {
276             a.super(name);
277         }
278     }
279 
280     // Mixing up outer instances, proxies, and initializers
281     public static class Test16 {
282 
283         final String x = String.valueOf(new Object().hashCode());
284 
285         public void run() {
286 
287             final String y = String.valueOf(new Object().hashCode());
288 
289             class Sub {
290 
291                 final String z;
292 
293                 Sub(String z, int ignored) {
294                     this(z, (float)ignored);
295                 }
296 
297                 Sub(String z, float ignored) {
298                     this.z = z;
299                 }
300 
301                 Sub(String z, byte ignored) {
302                     super();
303                     this.z = z;
304                 }
305 
306                 Sub(String z, char ignored) {
307                     this(z, (int)ignored);
308                 }
309 
310                 String x() {
311                     return x;
312                 }
313 
314                 String y() {
315                     return y;
316                 }
317 
318                 String z() {
319                     return z;
320                 }
321             }
322 
323             final String z = String.valueOf(new Object().hashCode());
324 
325             final Sub[] subs = new Sub[] {
326                 new Sub(z, 1),
327                 new Sub(z, -1),
328                 new Sub(z, (float)0),
329                 new Sub(z, (byte)0),
330                 new Sub(z, (char)0)
331             };
332 
333             for (int i = 0; i < subs.length; i++) {
334                 //System.err.println("i = " + i);
335                 final Sub sub = subs[i];
336                 final String subx = sub.x();
337                 final String suby = sub.y();
338                 final String subz = sub.z();
339                 if (!x.equals(subx))
340                     throw new RuntimeException("x=" + x + " but sub[" + i + "].x()=" + subx);
341                 if (!y.equals(suby))
342                     throw new RuntimeException("y=" + y + " but sub[" + i + "].y()=" + suby);
343                 if (!z.equals(subz))
344                     throw new RuntimeException("z=" + z + " but sub[" + i + "].z()=" + subz);
345             }
346         }
347     }
348 
349     // Records
350     public class Test17 {
351 
352         record Rectangle(float length, float width) { }
353 
354         record StringHolder(String string) {
355             StringHolder {
356                 java.util.Objects.requireNonNull(string);
357             }
358         }
359 
360         record ValueHolder(int value) {
361             ValueHolder(float x) {
362                 if (Float.isNaN(x))
363                     throw new IllegalArgumentException();
364                 this((int)x);
365             }
366         }
367     }
368 
369     // Exceptions thrown by initializer block
370     public static class Test18 extends AtomicReference<Object> {
371 
372         {
373             if ((this.get().hashCode() % 3) == 0)
374                 throw new MyException();
375         }
376 
377         public Test18(Object obj) throws MyException {
378             super(obj);
379         }
380 
381         public Test18(boolean fail) throws MyException {
382             Object obj;
383             for (obj = new Object(); true; obj = new Object()) {
384                 if (((obj.hashCode() % 3) == 0) != fail)
385                     continue;
386                 break;
387             }
388             this(obj);
389         }
390 
391         public static class MyException extends Exception {
392         }
393     }
394 
395     // super()/this() within outer try block but inside inner class
396     public static class Test19 {
397         public Test19(int x) {
398             try {
399                 new Test1(x) {
400                     @Override
401                     public int hashCode() {
402                         return x ^ super.hashCode();
403                     }
404                 };
405             } catch (StackOverflowError e) {
406                 // ignore
407             }
408         }
409     }
410 
411     // we allow 'this' reference prior to super() for field assignments only
412     public static class Test20 {
413         private int x;
414         public Test20(short x) {
415             x = x;
416             super();
417         }
418         public Test20(int x) {
419             this.x = x;
420             super();
421         }
422         public Test20(char x) {
423             Test20.this.x = x;
424             super();
425         }
426         public Test20(byte y) {
427             x = y;
428             this((int)y);
429             this.x++;
430         }
431     }
432 
433     // allow creating and using local and anonymous classes before super()
434     // they will not have enclosing instances though
435     public static class Test21 {
436         public Test21(int x) {
437             Runnable r = new Runnable() {
438                 public void run() {
439                     this.hashCode();
440                 }
441             };
442             r.run();
443             super();
444             r.run();
445         }
446         public Test21(float x) {
447             class Foo {
448                 public void bar() {
449                     this.hashCode();
450                 }
451             };
452             new Foo().bar();
453             super();
454             new Foo().bar();
455         }
456     }
457 
458 
459     public static void main(String[] args) {
460         new Test0();
461         new Test1();
462         new Test1(7);
463         new Test2<Byte>();
464         new Test2<>(args);
465         new Test3();
466         new SuperInitGood(3).new Test5(3);
467         new SuperInitGood(3).new Test6();
468         SuperInitGood.test7("foo");
469         SuperInitGood.test8();
470         new Test9(new SuperInitGood(5), "abc");
471         new Test10(7);
472         new Test11(9);
473         new Test12();
474         new Test13();
475         Test14 t14 = new Test14();
476         assert t14.x == 0 && t14.y == -1 && t14.z == 13;
477         t14 = new Test14(7);
478         assert t14.x == 7 && t14.y == -1 && t14.z == 13;
479         new Test15c(new Test15("foo"), "bar");
480         new Test16().run();
481         new Test17.StringHolder("foo");
482         try {
483             new Test17.StringHolder(null);
484             throw new Error();
485         } catch (NullPointerException e) {
486             // expected
487         }
488         try {
489             new Test18(true);
490             assert false : "expected exception";
491         } catch (Test18.MyException e) {
492             // expected
493         }
494         try {
495             new Test18(false);
496         } catch (Test18.MyException e) {
497             assert false : "unexpected exception: " + e;
498         }
499         new Test19(123);
500         new Test20(123);
501         new Test21((int)123);
502         new Test21((float)123);
503     }
504 }