1 /*
2 * Copyright (c) 2024, 2025, 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 8333748 8349536
27 * @summary javap should not fail if reserved access flag bits are set to 1
28 * @library /tools/lib
29 * @modules jdk.jdeps/com.sun.tools.javap
30 * @run junit UndefinedAccessFlagTest
31 */
32
33 import org.junit.jupiter.params.ParameterizedTest;
34 import org.junit.jupiter.params.provider.EnumSource;
35 import toolbox.JavapTask;
36 import toolbox.Task;
37 import toolbox.ToolBox;
38
39 import java.lang.classfile.AccessFlags;
40 import java.lang.classfile.ClassModel;
41 import java.lang.classfile.FieldModel;
42 import java.lang.classfile.MethodModel;
43 import java.lang.classfile.attribute.InnerClassInfo;
44 import java.lang.classfile.attribute.InnerClassesAttribute;
45 import java.nio.file.Files;
46 import java.nio.file.Path;
47
48 import static java.lang.classfile.ClassFile.*;
49 import static org.junit.jupiter.api.Assertions.assertTrue;
50
51 public class UndefinedAccessFlagTest {
52
53 final ToolBox toolBox = new ToolBox();
54
55 enum TestLocation {
56 NONE(false), CLASS, FIELD, METHOD, INNER_CLASS(false);
57
58 final boolean fails;
59 TestLocation() { this(true); }
60 TestLocation(boolean fails) { this.fails = fails; }
61 }
62
63 @ParameterizedTest
64 @EnumSource(TestLocation.class)
65 void test(TestLocation location) throws Throwable {
66 var cf = of();
67 ClassModel cm;
68 try (var is = UndefinedAccessFlagTest.class.getResourceAsStream(
69 "/UndefinedAccessFlagTest$SampleInnerClass.class"
70 )) {
71 cm = cf.parse(is.readAllBytes());
72 }
73 var bytes = cf.transformClass(cm, (cb, ce) -> {
74 switch (ce) {
75 case AccessFlags flags when location == TestLocation.CLASS -> cb
76 .withFlags(flags.flagsMask() | ACC_PRIVATE);
77 case FieldModel f when location == TestLocation.FIELD -> cb
78 .transformField(f, (fb, fe) -> {
79 if (fe instanceof AccessFlags flags) {
80 fb.withFlags(flags.flagsMask() | ACC_SYNCHRONIZED);
81 } else {
82 fb.with(fe);
83 }
84 });
85 case MethodModel m when location == TestLocation.METHOD -> cb
86 .transformMethod(m, (mb, me) -> {
87 if (me instanceof AccessFlags flags) {
88 mb.withFlags(flags.flagsMask() | ACC_INTERFACE);
89 } else {
90 mb.with(me);
91 }
92 });
93 case InnerClassesAttribute attr when location == TestLocation.INNER_CLASS -> cb
94 .with(InnerClassesAttribute.of(attr.classes().stream()
95 .map(ic -> InnerClassInfo.of(ic.innerClass(), ic.outerClass(), ic.innerName(), ic.flagsMask() | ACC_SUPER))
96 .toList()));
97 default -> cb.with(ce);
98 }
99 });
100
101 Files.write(Path.of("transformed.class"), bytes);
102
103 var lines = new JavapTask(toolBox)
104 .classes("transformed.class")
105 .options("-c", "-p", "-v")
106 .run(location.fails ? Task.Expect.FAIL : Task.Expect.SUCCESS)
107 .writeAll()
108 .getOutputLines(Task.OutputKind.DIRECT);
109
110 // No termination when access flag error happens
111 assertTrue(lines.stream().anyMatch(l -> l.contains("java.lang.String field;")));
112 assertTrue(lines.stream().anyMatch(l -> l.contains("UndefinedAccessFlagTest$SampleInnerClass();")));
113 assertTrue(lines.stream().anyMatch(l -> l.contains("void method();")));
114 assertTrue(lines.stream().anyMatch(l -> l.contains("SampleInnerClass=class UndefinedAccessFlagTest$SampleInnerClass of class UndefinedAccessFlagTest")));
115
116 // Remove non-error lines
117 assertTrue(lines.removeIf(st -> !st.startsWith("Error:")));
118 // Desired locations has errors
119 assertTrue(location == TestLocation.NONE || !lines.isEmpty());
120 // Access Flag errors only
121 assertTrue(lines.stream().allMatch(l -> l.contains("Access Flags:")), () -> String.join("\n", lines));
122 }
123
124 static class SampleInnerClass {
125 String field;
126 void method() {}
127 }
128 }
--- EOF ---