1 /*
2 * Copyright (c) 2017, 2018, 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 8182450
27 * @summary Bad classfiles should not abort compilations
28 * @library /tools/lib
29 * @modules
30 * jdk.compiler/com.sun.tools.javac.api
31 * jdk.compiler/com.sun.tools.javac.code
32 * jdk.compiler/com.sun.tools.javac.comp
33 * jdk.compiler/com.sun.tools.javac.jvm
34 * jdk.compiler/com.sun.tools.javac.main
35 * jdk.compiler/com.sun.tools.javac.processing
36 * jdk.compiler/com.sun.tools.javac.util
37 * @build toolbox.ToolBox toolbox.JavacTask
38 * @run main NoAbortForBadClassFile
39 */
40
41 import java.nio.file.Files;
42 import java.nio.file.Path;
43 import java.nio.file.Paths;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.stream.Collectors;
49 import java.util.stream.Stream;
50
51 import com.sun.tools.javac.api.JavacTaskImpl;
52 import com.sun.tools.javac.api.JavacTool;
53 import com.sun.tools.javac.code.DeferredCompletionFailureHandler;
54 import com.sun.tools.javac.code.Flags;
55 import com.sun.tools.javac.code.Symbol.ClassSymbol;
56 import com.sun.tools.javac.code.Symbol.CompletionFailure;
57 import com.sun.tools.javac.code.Symtab;
58 import com.sun.tools.javac.jvm.ClassReader;
59 import com.sun.tools.javac.util.Context;
60 import com.sun.tools.javac.util.Context.Factory;
61 import com.sun.tools.javac.util.Names;
62 import com.sun.tools.javac.util.Options;
63 import toolbox.Task;
64 import toolbox.Task.Expect;
65
66 import toolbox.TestRunner;
67 import toolbox.ToolBox;
68
69 public class NoAbortForBadClassFile extends TestRunner {
70
71 private ToolBox tb = new ToolBox();
72
73 public NoAbortForBadClassFile() {
74 super(System.out);
75 }
76
77 public static void main(String... args) throws Exception {
78 new NoAbortForBadClassFile().runTests(m -> new Object[] { Paths.get(m.getName()) });
79 }
80
81 @Test
82 public void testBrokenClassFile(Path base) throws Exception {
83 Path classes = base.resolve("classes");
84 Path brokenClassFile = classes.resolve("test").resolve("Broken.class");
85
86 Files.createDirectories(brokenClassFile.getParent());
87 Files.newOutputStream(brokenClassFile).close();
88
89 Path src = base.resolve("src");
90 tb.writeJavaFiles(src,
91 "package test; public class Test { private void test() { Broken b; String.unknown(); } }");
92 Path out = base.resolve("out");
93 tb.createDirectories(out);
94
95 List<String> log = new toolbox.JavacTask(tb)
96 .options("-classpath", classes.toString(),
97 "-XDrawDiagnostics")
98 .outdir(out)
99 .files(tb.findJavaFiles(src))
100 .run(Expect.FAIL)
101 .writeAll()
102 .getOutputLines(Task.OutputKind.DIRECT);
103
104 List<String> expectedOut = Arrays.asList(
105 "Test.java:1:57: compiler.err.cant.access: test.Broken, (compiler.misc.bad.class.file.header: Broken.class, (compiler.misc.bad.class.truncated.at.offset: 0))",
106 "Test.java:1:73: compiler.err.cant.resolve.location.args: kindname.method, unknown, , , (compiler.misc.location: kindname.class, java.lang.String, null)",
107 "2 errors"
108 );
109
110 if (!expectedOut.equals(log))
111 throw new Exception("expected output not found: " + log);
112 }
113
114 @Test
115 public void testLoading(Path base) throws Exception {
116 Path src = base.resolve("src");
117 tb.writeJavaFiles(src,
118 "public class Test { static { new Object() {}; } public static class I { public static class II { } } }");
119 Path out = base.resolve("out");
120 tb.createDirectories(out);
121
122 new toolbox.JavacTask(tb)
123 .outdir(out)
124 .options("-source", "10", "-target", "10")
125 .files(tb.findJavaFiles(src))
126 .run(Expect.SUCCESS)
127 .writeAll()
128 .getOutputLines(Task.OutputKind.DIRECT);
129
130 List<Path> files;
131 try (Stream<Path> dir = Files.list(out)) {
132 files = dir.collect(Collectors.toList());
133 }
134
135 List<List<Path>> result = new ArrayList<>();
136
137 permutations(files, Collections.emptyList(), result);
138
139 int testNum = 0;
140
141 for (List<Path> order : result) {
142 for (Path missing : order) {
143 Path test = base.resolve(String.valueOf(testNum++)).resolve("test");
144
145 tb.createDirectories(test);
146
147 for (Path p : order) {
148 Files.copy(p, test.resolve(p.getFileName()));
149 }
150
151 List<String> actual = complete(test, order, missing, true);
152
153 Files.delete(test.resolve(missing.getFileName()));
154
155 List<String> expected = complete(test, order, missing, false);
156
157 if (!actual.equals(expected)) {
158 throw new AssertionError("Unexpected state, actual=\n" + actual + "\nexpected=\n" + expected + "\norder=" + order + "\nmissing=" + missing);
159 }
160 }
161 }
162 }
163
164 private static void permutations(List<Path> todo, List<Path> currentList, List<List<Path>> result) {
165 if (todo.isEmpty()) {
166 result.add(currentList);
167 return ;
168 }
169
170 for (Path p : todo) {
171 List<Path> nextTODO = new ArrayList<>(todo);
172
173 nextTODO.remove(p);
174
175 List<Path> nextList = new ArrayList<>(currentList);
176
177 nextList.add(p);
178
179 permutations(nextTODO, nextList, result);
180 }
181 }
182
183 private List<String> complete(Path test, List<Path> order, Path missing, boolean badClassFile) {
184 Context context = new Context();
185 if (badClassFile) {
186 TestClassReader.preRegister(context);
187 }
188 JavacTool tool = JavacTool.create();
189 JavacTaskImpl task = (JavacTaskImpl) tool.getTask(null, null, null, List.of("-classpath", test.toString(), "-XDblockClass=" + flatName(missing)), null, null, context);
190 Symtab syms = Symtab.instance(context);
191 Names names = Names.instance(context);
192
193 DeferredCompletionFailureHandler dcfh = DeferredCompletionFailureHandler.instance(context);
194
195 dcfh.setHandler(dcfh.javacCodeHandler);
196
197 task.getElements().getTypeElement("java.lang.Object");
198
199 if (!badClassFile) {
200 //to ensure the same paths taken in ClassFinder.completeEnclosing in case the file is missing:
201 syms.enterClass(syms.unnamedModule, names.fromString(flatName(missing)));
202 }
203
204 List<String> result = new ArrayList<>();
205
206 for (Path toCheck : order) {
207 ClassSymbol sym = syms.enterClass(syms.unnamedModule, names.fromString(flatName(toCheck)));
208
209 try {
210 sym.complete();
211 } catch (CompletionFailure ignore) {
212 }
213
214 long flags = sym.flags_field;
215
216 flags &= ~(Flags.CLASS_SEEN | Flags.SOURCE_SEEN | Flags.IDENTITY_TYPE); // earlier ACC_SUPER was dropped by javac.
217
218 result.add("sym: " + sym.flatname + ", " + sym.owner.flatName() +
219 ", " + sym.type + ", " + sym.members_field + ", " + flags);
220 }
221
222 return result;
223 }
224
225 private String flatName(Path p) {
226 return p.getFileName().toString().replace(".class", "");
227 }
228
229 private static class TestClassReader extends ClassReader {
230 public static void preRegister(Context ctx) {
231 ctx.put(classReaderKey, (Factory<ClassReader>) c -> new TestClassReader(ctx));
232 }
233
234 private final String block;
235
236 public TestClassReader(Context context) {
237 super(context);
238 block = Options.instance(context).get("blockClass");
239 }
240
241 @Override
242 public void readClassFile(ClassSymbol c) {
243 super.readClassFile(c);
244
245 if (c.flatname.contentEquals(block)) {
246 throw badClassFile("blocked");
247 }
248 }
249
250 }
251
252 }
--- EOF ---