2
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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.
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
27
* @summary javap -m <module> cannot read a module-info.class
31
* jdk.compiler/com.sun.tools.javac.api
32
* jdk.compiler/com.sun.tools.javac.main
33
* jdk.jdeps/com.sun.tools.javap
34
* @build toolbox.JavacTask toolbox.JavapTask toolbox.ToolBox toolbox.TestRunner
35
* @run main TestClassNameWarning
38
import java.lang.constant.ClassDesc;
39
import java.nio.file.Files;
40
import java.nio.file.Path;
41
import java.nio.file.Paths;
42
import java.util.Arrays;
44
import java.util.regex.Pattern;
45
import java.util.stream.Collectors;
48
import java.lang.classfile.*;
49
import toolbox.JavacTask;
50
import toolbox.JavapTask;
52
import toolbox.TestRunner;
53
import toolbox.ToolBox;
55
public class TestClassNameWarning extends TestRunner {
56
public static void main(String... args) throws Exception {
57
new TestClassNameWarning().runTests(m -> new Object[] { Paths.get(m.getName()) });
60
private ToolBox tb = new ToolBox();
62
TestClassNameWarning() {
67
* Baseline test for normal classes.
70
public void testStandardClass(Path base) throws Exception {
71
Path src = base.resolve("src");
72
Path classes = Files.createDirectories(base.resolve("classes"));
73
tb.writeJavaFiles(src, "class A { }");
76
.outdir(classes.toString())
77
.files(tb.findJavaFiles(src))
81
List<String> log = new JavapTask(tb)
82
.classpath(classes.toString())
86
.getOutputLines(Task.OutputKind.DIRECT);
88
checkOutput(log, false, "^(Warning|Error)");
89
checkOutput(log, true, "class A");
93
* Test that module-info can be used to name the .class file
94
* for a module declaration (i.e. ACC_MODULE, this_class == 0)
95
* This is the primary test case for the bug as reported.
98
public void testStandardModuleInfo(Path base) throws Exception {
99
Path src = base.resolve("src");
100
Path classes = Files.createDirectories(base.resolve("classes"));
101
tb.writeJavaFiles(src, "module m { }");
104
.outdir(classes.toString())
105
.files(tb.findJavaFiles(src))
109
List<String> log = new JavapTask(tb)
110
.options("--module-path", classes.toString(),
112
.classes("module-info")
115
.getOutputLines(Task.OutputKind.DIRECT);
117
checkOutput(log, false, "^(Warning|Error)");
118
checkOutput(log, true, "module m");
122
* Test module-info can still be used to find a weirdly named
123
* class equivalent to "class module-info { }" if that were legal in JLS.
124
* Such a class file would arguably be legal in certain selected contexts.
127
public void testLegacyModuleInfo(Path base) throws Exception {
128
Path src = base.resolve("src");
129
Path classes = Files.createDirectories(base.resolve("classes"));
130
tb.writeJavaFiles(src, "class module_info { }");
133
.outdir(classes.toString())
134
.files(tb.findJavaFiles(src))
138
byte[] bytes = Files.readAllBytes(classes.resolve("module_info.class"));
139
byte[] searchBytes = "module_info".getBytes("UTF-8");
140
byte[] replaceBytes = "module-info".getBytes("UTF-8");
141
for (int i = 0; i < bytes.length - searchBytes.length; i++) {
142
if (Arrays.equals(bytes, i, i + searchBytes.length,
143
searchBytes, 0, searchBytes.length)) {
144
System.arraycopy(replaceBytes, 0, bytes, i, replaceBytes.length);
147
Files.write(classes.resolve("module-info.class"), bytes);
149
List<String> log = new JavapTask(tb)
150
.classpath(classes.toString())
151
.options("-bootclasspath", "") // hide all system classes
152
.classes("module-info")
155
.getOutputLines(Task.OutputKind.DIRECT);
157
checkOutput(log, false, "^(Warning|Error)");
158
checkOutput(log, true, "class module-info");
162
* Test an invalid class, with this_class == 0.
165
public void testNoNameClass(Path base) throws Exception {
166
Path src = base.resolve("src");
167
Path classes = Files.createDirectories(base.resolve("classes"));
168
tb.writeJavaFiles(src, "class A { }");
171
.outdir(classes.toString())
172
.files(tb.findJavaFiles(src))
175
ClassModel cm = ClassFile.of().parse(classes.resolve("A.class"));
176
ClassFile.of().buildTo(
177
classes.resolve("Z.class"),
178
ClassDesc.of("0"), cb -> {
179
for (ClassElement ce : cm) {
185
List<String> log = new JavapTask(tb)
186
.classpath(classes.toString())
190
.getOutputLines(Task.OutputKind.DIRECT);
192
checkOutput(log, true, "Warning:.*Z.class does not contain class Z");
196
* Test a class with unexpected contents.
197
* This is the arguably the most common negative case.
200
public void testWrongNameClass(Path base) throws Exception {
201
Path src = base.resolve("src");
202
Path classes = Files.createDirectories(base.resolve("classes"));
203
tb.writeJavaFiles(src, "class A { }");
206
.outdir(classes.toString())
207
.files(tb.findJavaFiles(src))
211
Files.move(classes.resolve("A.class"), classes.resolve("B.class"));
213
List<String> log = new JavapTask(tb)
214
.classpath(classes.toString())
218
.getOutputLines(Task.OutputKind.DIRECT);
220
checkOutput(log, true, "Warning:.*B.class does not contain class B");
224
* Check that the output does, or does not, contain lines matching a regex.
226
private void checkOutput(List<String> log, boolean expect, String regex) {
227
Pattern p = Pattern.compile(regex);
228
List<String> matches = log.stream()
229
.filter(line -> p.matcher(line).find())
230
.collect(Collectors.toList());
232
if (matches.isEmpty()) {
233
error("expected output not found: " + regex);
236
if (!matches.isEmpty()) {
237
error("unexpected output found: " + matches);