/* * 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 8289106 8293627 * @summary Tests of AccessFlag.locations(ClassFileFormatVersion) */ import java.lang.reflect.AccessFlag; import static java.lang.reflect.AccessFlag.*; import java.lang.reflect.ClassFileFormatVersion; import java.util.HashSet; import java.util.Set; /* * There are several patterns of access flag applicability. First, an * access flag can be applied to the same set of locations for each * class file format version. This is "invariant" usage. Second, an * access flag can be defined for version N, therefore inapplicable * for earlier versions, and then applied to the same locations for * all subsequent versions. This is "step" usage. Finally, an access * flag to have a more complicated pattern, having multiple steps of * being allowed at more locations or even having locations removed if * the access flag is retired. * * List of access flags and how they are tested: * * PUBLIC step * PRIVATE step * PROTECTED step * STATIC step * FINAL two-step * SUPER invariant * OPEN step * TRANSITIVE step * SYNCHRONIZED invariant * STATIC_PHASE step * VOLATILE invariant * BRIDGE step * TRANSIENT invariant * VARARGS step * NATIVE invariant * INTERFACE step * ABSTRACT step * STRICT other * SYNTHETIC other (three-step) * ANNOTATION step * ENUM step * MANDATED two-step * MODULE step */ public class VersionedLocationsTest { public static void main(String... args) throws Exception { testInvariantAccessFlags(); testStepFunctionAccessFlags(); testTwoStepAccessFlags(); testSynthetic(); testStrict(); testLatestMatch(); } /** * Invariant access flags have the same set of locations for each * class file format version. */ private static void testInvariantAccessFlags() { Set invariantAccessFlags = Set.of(SUPER, SYNCHRONIZED, VOLATILE, TRANSIENT, NATIVE); for(var accessFlag : invariantAccessFlags) { Set expected = accessFlag.locations(); for(var cffv : ClassFileFormatVersion.values()) { compareLocations(accessFlag.locations(), accessFlag, cffv); } } } private static void testStepFunctionAccessFlags() { StepFunctionTC[] testCases = { new StepFunctionTC(PUBLIC, removeInnerClass(PUBLIC.locations()), ClassFileFormatVersion.RELEASE_1), new StepFunctionTC(PRIVATE, removeInnerClass(PRIVATE.locations()), ClassFileFormatVersion.RELEASE_1), new StepFunctionTC(PROTECTED, removeInnerClass(PROTECTED.locations()), ClassFileFormatVersion.RELEASE_1), new StepFunctionTC(STATIC, removeInnerClass(STATIC.locations()), ClassFileFormatVersion.RELEASE_1), new StepFunctionTC(OPEN, Set.of(), ClassFileFormatVersion.RELEASE_9), new StepFunctionTC(TRANSITIVE, Set.of(), ClassFileFormatVersion.RELEASE_9), new StepFunctionTC(STATIC_PHASE, Set.of(), ClassFileFormatVersion.RELEASE_9), new StepFunctionTC(BRIDGE, Set.of(), ClassFileFormatVersion.RELEASE_5), new StepFunctionTC(VARARGS, Set.of(), ClassFileFormatVersion.RELEASE_5), new StepFunctionTC(INTERFACE, removeInnerClass(INTERFACE.locations()), ClassFileFormatVersion.RELEASE_1), new StepFunctionTC(ABSTRACT, removeInnerClass(ABSTRACT.locations()), ClassFileFormatVersion.RELEASE_1), new StepFunctionTC(ANNOTATION, Set.of(), ClassFileFormatVersion.RELEASE_5), new StepFunctionTC(ENUM, Set.of(), ClassFileFormatVersion.RELEASE_5), new StepFunctionTC(MODULE, Set.of(), ClassFileFormatVersion.RELEASE_9) }; for (var testCase : testCases) { for (var cffv : ClassFileFormatVersion.values()) { compareLocations(cffv.compareTo(testCase.transition()) >= 0 ? testCase.finalLocs() : testCase.initialLocs(), testCase.accessFlag, cffv); } } } private static void compareLocations(Set expected, AccessFlag accessFlag, ClassFileFormatVersion cffv) { var actual = accessFlag.locations(cffv); if (!expected.equals(actual)) { throw new RuntimeException("Unexpected locations for " + accessFlag + " on " + cffv + "\n" + "Expected " + expected + "; got \t" + actual); } } private static Set removeInnerClass(Set locations) { var s = new HashSet<>(locations); s.remove(Location.INNER_CLASS); return s; } private record StepFunctionTC(AccessFlag accessFlag, Set initialLocs, ClassFileFormatVersion transition) { public Set finalLocs() { return accessFlag.locations(); } } private record TwoStepFunctionTC(AccessFlag accessFlag, Set initialLocs, ClassFileFormatVersion transition1, Set firstLocs, ClassFileFormatVersion transition2) { public Set secondLocs() { return accessFlag.locations(); } } private static void testTwoStepAccessFlags() { TwoStepFunctionTC[] testCases = { new TwoStepFunctionTC(FINAL, Set.of(Location.CLASS, Location.FIELD, Location.METHOD), ClassFileFormatVersion.RELEASE_1, Set.of(Location.CLASS, Location.FIELD, Location.METHOD, Location.INNER_CLASS), ClassFileFormatVersion.RELEASE_8), new TwoStepFunctionTC(MANDATED, Set.of(), ClassFileFormatVersion.RELEASE_8, Set.of(Location.METHOD_PARAMETER), ClassFileFormatVersion.RELEASE_9), }; for (var testCase : testCases) { for (var cffv : ClassFileFormatVersion.values()) { var transition1 = testCase.transition1(); var transition2 = testCase.transition2(); Set expected; if (cffv.compareTo(transition1) < 0) { expected = testCase.initialLocs(); } else if (cffv.compareTo(transition1) >= 0 && cffv.compareTo(transition2) < 0) { expected = testCase.firstLocs(); } else { // cffv >= transition2 expected = testCase.secondLocs(); } compareLocations(expected, testCase.accessFlag(), cffv); } } } private static void testSynthetic() { for (var cffv : ClassFileFormatVersion.values()) { Set expected; if (cffv.compareTo(ClassFileFormatVersion.RELEASE_6) <= 0) { expected = Set.of(); } else { expected = switch(cffv) { case RELEASE_7 -> Set.of(Location.CLASS, Location.FIELD, Location.METHOD, Location.INNER_CLASS); case RELEASE_8 -> Set.of(Location.CLASS, Location.FIELD, Location.METHOD, Location.INNER_CLASS, Location.METHOD_PARAMETER); default -> SYNTHETIC.locations(); }; } compareLocations(expected, SYNTHETIC, cffv); } } private static void testStrict() { for (var cffv : ClassFileFormatVersion.values()) { Set expected = (cffv.compareTo(ClassFileFormatVersion.RELEASE_2) >= 0 && cffv.compareTo(ClassFileFormatVersion.RELEASE_16) <= 0) ? Set.of(Location.METHOD) : Set.of(); compareLocations(expected, STRICT, cffv); } } private static void testLatestMatch() { // Verify accessFlag.locations() and // accessFlag.locations(ClassFileFormatVersion.latest()) are // consistent var LATEST = ClassFileFormatVersion.latest(); for (var accessFlag : AccessFlag.values()) { var locationSet = accessFlag.locations(); var locationLatestSet = accessFlag.locations(LATEST); if (!locationSet.equals(locationLatestSet)) { throw new RuntimeException("Unequal location sets for " + accessFlag); } } } }