NoAbortForBadClassFile.java revision 4254:d601b22360fa
19313Ssos/*
29313Ssos * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
39313Ssos * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
49313Ssos *
59313Ssos * This code is free software; you can redistribute it and/or modify it
69313Ssos * under the terms of the GNU General Public License version 2 only, as
79313Ssos * published by the Free Software Foundation.
89313Ssos *
9111798Sdes * This code is distributed in the hope that it will be useful, but WITHOUT
109313Ssos * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
119313Ssos * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
129313Ssos * version 2 for more details (a copy is included in the LICENSE file that
139313Ssos * accompanied this code).
149313Ssos *
1597748Sschweikh * You should have received a copy of the GNU General Public License version
169313Ssos * 2 along with this work; if not, write to the Free Software Foundation,
179313Ssos * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
189313Ssos *
199313Ssos * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
209313Ssos * or visit www.oracle.com if you need additional information or have any
219313Ssos * questions.
229313Ssos */
239313Ssos
249313Ssos/**
259313Ssos * @test
269313Ssos * @bug 8182450
279313Ssos * @summary Bad classfiles should not abort compilations
289313Ssos * @library /tools/lib
29116173Sobrien * @modules
30116173Sobrien *      jdk.compiler/com.sun.tools.javac.api
31116173Sobrien *      jdk.compiler/com.sun.tools.javac.code
32156874Sru *      jdk.compiler/com.sun.tools.javac.comp
33101189Srwatson *      jdk.compiler/com.sun.tools.javac.jvm
3431784Seivind *      jdk.compiler/com.sun.tools.javac.main
359313Ssos *      jdk.compiler/com.sun.tools.javac.processing
369313Ssos *      jdk.compiler/com.sun.tools.javac.util
3776166Smarkm * @build toolbox.ToolBox toolbox.JavacTask
3876166Smarkm * @run main NoAbortForBadClassFile
399313Ssos */
409313Ssos
419313Ssosimport java.nio.file.Files;
4231561Sbdeimport java.nio.file.Path;
43101189Srwatsonimport java.nio.file.Paths;
449313Ssosimport java.util.ArrayList;
4572538Sjlemonimport java.util.Arrays;
4676166Smarkmimport java.util.Collections;
4776166Smarkmimport java.util.List;
48102814Siedowseimport java.util.stream.Collectors;
4976166Smarkmimport java.util.stream.Stream;
5014331Speter
5176166Smarkmimport com.sun.tools.javac.api.JavacTaskImpl;
5212458Sbdeimport com.sun.tools.javac.api.JavacTool;
5372538Sjlemonimport com.sun.tools.javac.code.Flags;
5472538Sjlemonimport com.sun.tools.javac.code.Symbol.ClassSymbol;
5572538Sjlemonimport com.sun.tools.javac.code.Symbol.CompletionFailure;
5672538Sjlemonimport com.sun.tools.javac.code.Symtab;
57140214Sobrienimport com.sun.tools.javac.jvm.ClassReader;
58140214Sobrienimport com.sun.tools.javac.util.Context;
59140214Sobrienimport com.sun.tools.javac.util.Context.Factory;
60140214Sobrienimport com.sun.tools.javac.util.Names;
6164905Smarcelimport com.sun.tools.javac.util.Options;
6268583Smarcelimport toolbox.Task;
63133816Stjrimport toolbox.Task.Expect;
6464905Smarcel
659313Ssosimport toolbox.TestRunner;
669313Ssosimport toolbox.ToolBox;
6783366Sjulian
689313Ssospublic class NoAbortForBadClassFile extends TestRunner {
69102814Siedowse
70102814Siedowse    private ToolBox tb = new ToolBox();
719313Ssos
72102814Siedowse    public NoAbortForBadClassFile() {
7314331Speter        super(System.out);
749313Ssos    }
7572543Sjlemon
76102814Siedowse    public static void main(String... args) throws Exception {
779313Ssos        new NoAbortForBadClassFile().runTests(m -> new Object[] { Paths.get(m.getName()) });
78102814Siedowse    }
79102814Siedowse
80102814Siedowse    @Test
81102814Siedowse    public void testBrokenClassFile(Path base) throws Exception {
829313Ssos        Path classes = base.resolve("classes");
839313Ssos        Path brokenClassFile = classes.resolve("test").resolve("Broken.class");
849313Ssos
8583366Sjulian        Files.createDirectories(brokenClassFile.getParent());
869313Ssos        Files.newOutputStream(brokenClassFile).close();
8783382Sjhb
88102814Siedowse        Path src = base.resolve("src");
89102814Siedowse        tb.writeJavaFiles(src,
9014331Speter                          "package test; public class Test { private void test() { Broken b; String.unknown(); } }");
9114331Speter        Path out = base.resolve("out");
92102814Siedowse        tb.createDirectories(out);
9314331Speter
94102814Siedowse        List<String> log = new toolbox.JavacTask(tb)
9514331Speter                .options("-classpath", classes.toString(),
969313Ssos                         "-XDrawDiagnostics")
9772543Sjlemon                .outdir(out)
9872543Sjlemon                .files(tb.findJavaFiles(src))
99102814Siedowse                .run(Expect.FAIL)
1009313Ssos                .writeAll()
101102814Siedowse                .getOutputLines(Task.OutputKind.DIRECT);
1029313Ssos
103102814Siedowse        List<String> expectedOut = Arrays.asList(
104111798Sdes                "Test.java:1:57: compiler.err.cant.access: test.Broken, (compiler.misc.bad.class.file.header: Broken.class, (compiler.misc.class.file.wrong.class: java.lang.AutoCloseable))",
105102814Siedowse                 "Test.java:1:73: compiler.err.cant.resolve.location.args: kindname.method, unknown, , , (compiler.misc.location: kindname.class, java.lang.String, null)",
1069313Ssos                 "2 errors"
107102814Siedowse        );
1089313Ssos
109102814Siedowse        if (!expectedOut.equals(log))
1109313Ssos            throw new Exception("expected output not found: " + log);
111102814Siedowse    }
1129313Ssos
113102814Siedowse    @Test
1149313Ssos    public void testLoading(Path base) throws Exception {
115102814Siedowse        Path src = base.resolve("src");
1169313Ssos        tb.writeJavaFiles(src,
117102814Siedowse                          "public class Test { static { new Object() {}; } public static class I { public static class II { } } }");
1189313Ssos        Path out = base.resolve("out");
119102814Siedowse        tb.createDirectories(out);
1209313Ssos
121102814Siedowse        new toolbox.JavacTask(tb)
1229313Ssos                .outdir(out)
123102814Siedowse                .files(tb.findJavaFiles(src))
1249313Ssos                .run(Expect.SUCCESS)
125102814Siedowse                .writeAll()
1269313Ssos                .getOutputLines(Task.OutputKind.DIRECT);
127102814Siedowse
12870061Sjhb        List<Path> files;
129102814Siedowse        try (Stream<Path> dir = Files.list(out)) {
1309313Ssos            files = dir.collect(Collectors.toList());
13189306Salfred        }
1329313Ssos
133113917Sjhb        List<List<Path>> result = new ArrayList<>();
13489319Salfred
13589319Salfred        permutations(files, Collections.emptyList(), result);
13689319Salfred
137102003Srwatson        for (List<Path> order : result) {
138102003Srwatson            for (Path missing : order) {
13989319Salfred                Path test = base.resolve("test");
14089319Salfred
14191140Stanimura                if (Files.exists(test)) {
14270061Sjhb                    tb.cleanDirectory(test);
14314331Speter                } else {
14472543Sjlemon                    tb.createDirectories(test);
14572543Sjlemon                }
14614331Speter
14791140Stanimura                for (Path p : order) {
148102814Siedowse                    Files.copy(p, test.resolve(p.getFileName()));
1499313Ssos                }
1509313Ssos
1519313Ssos                List<String> actual = complete(test, order, missing, true);
1529313Ssos
15383366Sjulian                Files.delete(test.resolve(missing.getFileName()));
1549313Ssos
1559313Ssos                List<String> expected = complete(test, order, missing, false);
15612858Speter
15712858Speter                if (!actual.equals(expected)) {
1589313Ssos                    throw new AssertionError("Unexpected state, actual=\n" + actual + "\nexpected=\n" + expected + "\norder=" + order + "\nmissing=" + missing);
15912858Speter                }
1609313Ssos            }
16112858Speter        }
1629313Ssos    }
1639313Ssos
1649313Ssos    private static void permutations(List<Path> todo, List<Path> currentList, List<List<Path>> result) {
16572543Sjlemon        if (todo.isEmpty()) {
16672543Sjlemon            result.add(currentList);
16783221Smarcel            return ;
1689313Ssos        }
16912858Speter
17012858Speter        for (Path p : todo) {
1719313Ssos            List<Path> nextTODO = new ArrayList<>(todo);
17283366Sjulian
1739313Ssos            nextTODO.remove(p);
1749313Ssos
1759313Ssos            List<Path> nextList = new ArrayList<>(currentList);
17614331Speter
17783366Sjulian            nextList.add(p);
17814331Speter
17914331Speter            permutations(nextTODO, nextList, result);
18014331Speter        }
18114331Speter    }
18214331Speter
18314331Speter    private List<String> complete(Path test, List<Path> order, Path missing, boolean badClassFile) {
18472543Sjlemon        Context context = new Context();
18572543Sjlemon        if (badClassFile) {
18672543Sjlemon            TestClassReader.preRegister(context);
18714331Speter        }
18814331Speter        JavacTool tool = JavacTool.create();
18914331Speter        JavacTaskImpl task = (JavacTaskImpl) tool.getTask(null, null, null, List.of("-classpath", test.toString(), "-XDblockClass=" + flatName(missing)), null, null, context);
19014331Speter        Symtab syms = Symtab.instance(context);
19114331Speter        Names names = Names.instance(context);
19214331Speter
19314331Speter        task.getElements().getTypeElement("java.lang.Object");
19483366Sjulian
19514331Speter        if (!badClassFile) {
19614331Speter            //to ensure the same paths taken in ClassFinder.completeEnclosing in case the file is missing:
197111797Sdes            syms.enterClass(syms.unnamedModule, names.fromString(flatName(missing)));
19814331Speter        }
19914331Speter
20083366Sjulian        List<String> result = new ArrayList<>();
20114331Speter
20214331Speter        for (Path toCheck : order) {
20314331Speter            ClassSymbol sym = syms.enterClass(syms.unnamedModule, names.fromString(flatName(toCheck)));
2049313Ssos
20583366Sjulian            try {
2069313Ssos                sym.complete();
20714331Speter            } catch (CompletionFailure ignore) {
20814331Speter            }
20914331Speter
21014331Speter            long flags = sym.flags_field;
21114331Speter
21283366Sjulian            flags &= ~(Flags.CLASS_SEEN | Flags.SOURCE_SEEN);
21314331Speter
21414331Speter            result.add("sym: " + sym.flatname + ", " + sym.owner.flatName() +
21583221Smarcel                       ", " + sym.type + ", " + sym.members_field + ", " + flags);
21683221Smarcel        }
21783221Smarcel
21883221Smarcel        return result;
21983221Smarcel    }
22083221Smarcel
22183221Smarcel    private String flatName(Path p) {
22283221Smarcel        return p.getFileName().toString().replace(".class", "");
22383221Smarcel    }
22483221Smarcel
22583221Smarcel    private static class TestClassReader extends ClassReader {
22683221Smarcel        public static void preRegister(Context ctx) {
22783221Smarcel            ctx.put(classReaderKey, (Factory<ClassReader>) c -> new TestClassReader(ctx));
22883221Smarcel        }
22983221Smarcel
23083221Smarcel        private final String block;
23183221Smarcel
23283221Smarcel        public TestClassReader(Context context) {
23383221Smarcel            super(context);
23483221Smarcel            block = Options.instance(context).get("blockClass");
23583221Smarcel        }
23683221Smarcel
23783221Smarcel        @Override
23883221Smarcel        public void readClassFile(ClassSymbol c) {
23983221Smarcel            super.readClassFile(c);
24083221Smarcel
24183221Smarcel            if (c.flatname.contentEquals(block)) {
24283221Smarcel                throw badClassFile("blocked");
24383221Smarcel            }
24483221Smarcel        }
24583366Sjulian
24683221Smarcel    }
24714331Speter
248111798Sdes}
24983221Smarcel