/* * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 8194743 * @summary Test valid placements of super()/this() in constructors * @enablePreview */ import java.util.concurrent.atomic.AtomicReference; public class SuperInitGood { SuperInitGood(Object obj) { } SuperInitGood(int x) { } // Default constructor provided by compiler static class Test0 { } // No explicit calls to this()/super() static class Test1 { Test1() { } Test1(int a) { this.hashCode(); } } // Explicit calls to this()/super() static class Test2 { static int i; Test2() { this(0); } Test2(int i) { Test2.i = i; super(); } Test2(T obj) { this(java.util.Objects.hashCode(obj)); } public T get() { return null; } } // Explicit this()/super() with stuff in front static class Test3 { int x; final int y; final int z; Test3() { new Object().hashCode(); new Object().hashCode(); super(); this.x = new Object().hashCode(); this.y = new Object().hashCode() % 17; this.z = this.x + this.y; } } // Reference within constructor to outer class that's also my superclass class Test5 extends SuperInitGood { Test5(Object obj) { if (obj == null) throw new IllegalArgumentException(); super(SuperInitGood.this); // NOT a 'this' reference } } // Initialization blocks class Test6 { final long startTime; final int x; { this.x = 12; } Test6() { long now = System.nanoTime(); long then = now + 1000000L; while (System.nanoTime() < then) { try { Thread.sleep(1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } super(); this.startTime = now; } } // Mix up inner classes, proxies, and super() calls // Copied mostly from UnverifiableInitForNestedLocalClassTest.java public static void test7(final String arg) { final String inlined = " inlined "; class LocalClass { String m() { return "LocalClass " + arg + inlined; } class SubClass extends LocalClass { @Override String m() { return "SubClass " + arg + inlined; } } class SubSubClass extends SubClass { @Override String m() { return "SubSubClass " + arg + inlined; } } class AnotherLocal { class AnotherSub extends LocalClass { AnotherSub() { } AnotherSub(int x) { this((char)x); } AnotherSub(char y) { super(); } @Override String m() { return "AnotherSub " + arg + inlined; } } } } } // Anonymous inner class public static void test8() { new Test2(null) { @Override public Byte get() { return (byte)-1; } }; } // Qualified super() invocation public static class Test9 extends Test5 { public Test9(SuperInitGood implicit, Object obj) { obj.hashCode(); implicit.super(obj); } } // Copied from WhichImplicitThis6 public static class Test10 { private int i; public Test10(int i) {} public class Sub extends Test10 { public Sub() { super(i); // i is not inherited, so it is the enclosing i } } } // Two constructors where only one invokes super() public static class Test11 { public Test11() { } public Test11(int x) { super(); } } // Nested version of the previous test public static class Test12 { Test12() { class Sub { public Sub() { } public Sub(int j) { super(); } } } } // Nested super()'s requiring initialization code appended public static class Test13 extends SuperInitGood { final int x = new Object().hashCode(); Test13() { super(new Object() { public void foo() { class Bar { final int y = new Object().hashCode(); Bar() { super(); } Bar(int ignored) { } } } }); } } // Initializer in initializer block public static class Test14 { final int x; // initialized in constructor final int y; // initialized in initialization block final int z = 13; // initialized with intializer value public Test14() { this(0); } public Test14(boolean z) { this.x = z ? 1 : 0; } public Test14(int x) { super(); this.x = x; } { this.y = -1; } } // Qualified super() invocation with superclass instance public static class Test15 { final String name; public Test15(String name) { this.name = name; } public class Test15b extends Test15 { public Test15b(String name) { super(name); } public String getName() { return Test15.this.name; } } } public static class Test15c extends Test15.Test15b { public Test15c(Test15 a, String name) { a.super(name); } } // Mixing up outer instances, proxies, and initializers public static class Test16 { final String x = String.valueOf(new Object().hashCode()); public void run() { final String y = String.valueOf(new Object().hashCode()); class Sub { final String z; Sub(String z, int ignored) { this(z, (float)ignored); } Sub(String z, float ignored) { this.z = z; } Sub(String z, byte ignored) { super(); this.z = z; } Sub(String z, char ignored) { this(z, (int)ignored); } String x() { return x; } String y() { return y; } String z() { return z; } } final String z = String.valueOf(new Object().hashCode()); final Sub[] subs = new Sub[] { new Sub(z, 1), new Sub(z, -1), new Sub(z, (float)0), new Sub(z, (byte)0), new Sub(z, (char)0) }; for (int i = 0; i < subs.length; i++) { //System.err.println("i = " + i); final Sub sub = subs[i]; final String subx = sub.x(); final String suby = sub.y(); final String subz = sub.z(); if (!x.equals(subx)) throw new RuntimeException("x=" + x + " but sub[" + i + "].x()=" + subx); if (!y.equals(suby)) throw new RuntimeException("y=" + y + " but sub[" + i + "].y()=" + suby); if (!z.equals(subz)) throw new RuntimeException("z=" + z + " but sub[" + i + "].z()=" + subz); } } } // Records public class Test17 { record Rectangle(float length, float width) { } record StringHolder(String string) { StringHolder { java.util.Objects.requireNonNull(string); } } record ValueHolder(int value) { ValueHolder(float x) { if (Float.isNaN(x)) throw new IllegalArgumentException(); this((int)x); } } } // Exceptions thrown by initializer block public static class Test18 extends AtomicReference { { if ((this.get().hashCode() % 3) == 0) throw new MyException(); } public Test18(Object obj) throws MyException { super(obj); } public Test18(boolean fail) throws MyException { Object obj; for (obj = new Object(); true; obj = new Object()) { if (((obj.hashCode() % 3) == 0) != fail) continue; break; } this(obj); } public static class MyException extends Exception { } } // super()/this() within outer try block but inside inner class public static class Test19 { public Test19(int x) { try { new Test1(x) { @Override public int hashCode() { return x ^ super.hashCode(); } }; } catch (StackOverflowError e) { // ignore } } } // we allow 'this' reference prior to super() for field assignments only public static class Test20 { private int x; public Test20(short x) { x = x; super(); } public Test20(int x) { this.x = x; super(); } public Test20(char x) { Test20.this.x = x; super(); } public Test20(byte y) { x = y; this((int)y); this.x++; } } // allow creating and using local and anonymous classes before super() // they will not have enclosing instances though public static class Test21 { public Test21(int x) { Runnable r = new Runnable() { public void run() { this.hashCode(); } }; r.run(); super(); r.run(); } public Test21(float x) { class Foo { public void bar() { this.hashCode(); } }; new Foo().bar(); super(); new Foo().bar(); } } public static void main(String[] args) { new Test0(); new Test1(); new Test1(7); new Test2(); new Test2<>(args); new Test3(); new SuperInitGood(3).new Test5(3); new SuperInitGood(3).new Test6(); SuperInitGood.test7("foo"); SuperInitGood.test8(); new Test9(new SuperInitGood(5), "abc"); new Test10(7); new Test11(9); new Test12(); new Test13(); Test14 t14 = new Test14(); assert t14.x == 0 && t14.y == -1 && t14.z == 13; t14 = new Test14(7); assert t14.x == 7 && t14.y == -1 && t14.z == 13; new Test15c(new Test15("foo"), "bar"); new Test16().run(); new Test17.StringHolder("foo"); try { new Test17.StringHolder(null); throw new Error(); } catch (NullPointerException e) { // expected } try { new Test18(true); assert false : "expected exception"; } catch (Test18.MyException e) { // expected } try { new Test18(false); } catch (Test18.MyException e) { assert false : "unexpected exception: " + e; } new Test19(123); new Test20(123); new Test21((int)123); new Test21((float)123); } }