8131915: CompletionFailure during import listing crashes javac

Handling CompletionFailures during import listing properly.

Reviewed-by: mcimadamore
This commit is contained in:
Jan Lahoda 2015-08-03 13:28:39 +02:00
parent d2e2494f9e
commit 16d8f98d09
5 changed files with 160 additions and 46 deletions

View File

@ -27,7 +27,11 @@ package com.sun.tools.javac.code;
import com.sun.tools.javac.code.Kinds.Kind;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import com.sun.tools.javac.code.Symbol.CompletionFailure;
import com.sun.tools.javac.code.Symbol.TypeSymbol;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.util.*;
@ -35,8 +39,6 @@ import com.sun.tools.javac.util.List;
import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
import static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/** A scope represents an area of visibility in a Java program. The
* Scope class is a container for symbols which provides
@ -721,8 +723,8 @@ public abstract class Scope {
prependSubScope(currentFileScope);
}
public Scope importByName(Types types, Scope origin, Name name, ImportFilter filter) {
return appendScope(new FilterImportScope(types, origin, name, filter, true));
public Scope importByName(Types types, Scope origin, Name name, ImportFilter filter, JCImport imp, BiConsumer<JCImport, CompletionFailure> cfHandler) {
return appendScope(new FilterImportScope(types, origin, name, filter, imp, cfHandler));
}
public Scope importType(Scope delegate, Scope origin, Symbol sym) {
@ -786,15 +788,16 @@ public abstract class Scope {
public void importAll(Types types, Scope origin,
ImportFilter filter,
boolean staticImport) {
JCImport imp,
BiConsumer<JCImport, CompletionFailure> cfHandler) {
for (Scope existing : subScopes) {
Assert.check(existing instanceof FilterImportScope);
FilterImportScope fis = (FilterImportScope) existing;
if (fis.origin == origin && fis.filter == filter &&
fis.staticImport == staticImport)
fis.imp.staticImport == imp.staticImport)
return ; //avoid entering the same scope twice
}
prependSubScope(new FilterImportScope(types, origin, null, filter, staticImport));
prependSubScope(new FilterImportScope(types, origin, null, filter, imp, cfHandler));
}
public boolean isFilled() {
@ -813,32 +816,40 @@ public abstract class Scope {
private final Scope origin;
private final Name filterName;
private final ImportFilter filter;
private final boolean staticImport;
private final JCImport imp;
private final BiConsumer<JCImport, CompletionFailure> cfHandler;
public FilterImportScope(Types types,
Scope origin,
Name filterName,
ImportFilter filter,
boolean staticImport) {
JCImport imp,
BiConsumer<JCImport, CompletionFailure> cfHandler) {
super(origin.owner);
this.types = types;
this.origin = origin;
this.filterName = filterName;
this.filter = filter;
this.staticImport = staticImport;
this.imp = imp;
this.cfHandler = cfHandler;
}
@Override
public Iterable<Symbol> getSymbols(final Filter<Symbol> sf, final LookupKind lookupKind) {
if (filterName != null)
return getSymbolsByName(filterName, sf, lookupKind);
SymbolImporter si = new SymbolImporter(staticImport) {
@Override
Iterable<Symbol> doLookup(TypeSymbol tsym) {
return tsym.members().getSymbols(sf, lookupKind);
}
};
return si.importFrom((TypeSymbol) origin.owner) :: iterator;
try {
SymbolImporter si = new SymbolImporter(imp.staticImport) {
@Override
Iterable<Symbol> doLookup(TypeSymbol tsym) {
return tsym.members().getSymbols(sf, lookupKind);
}
};
return si.importFrom((TypeSymbol) origin.owner) :: iterator;
} catch (CompletionFailure cf) {
cfHandler.accept(imp, cf);
return Collections.emptyList();
}
}
@Override
@ -847,13 +858,18 @@ public abstract class Scope {
final LookupKind lookupKind) {
if (filterName != null && filterName != name)
return Collections.emptyList();
SymbolImporter si = new SymbolImporter(staticImport) {
@Override
Iterable<Symbol> doLookup(TypeSymbol tsym) {
return tsym.members().getSymbolsByName(name, sf, lookupKind);
}
};
return si.importFrom((TypeSymbol) origin.owner) :: iterator;
try {
SymbolImporter si = new SymbolImporter(imp.staticImport) {
@Override
Iterable<Symbol> doLookup(TypeSymbol tsym) {
return tsym.members().getSymbolsByName(name, sf, lookupKind);
}
};
return si.importFrom((TypeSymbol) origin.owner) :: iterator;
} catch (CompletionFailure cf) {
cfHandler.accept(imp, cf);
return Collections.emptyList();
}
}
@Override
@ -863,7 +879,7 @@ public abstract class Scope {
@Override
public boolean isStaticallyImported(Symbol byName) {
return staticImport;
return imp.staticImport;
}
abstract class SymbolImporter {

View File

@ -27,6 +27,7 @@ package com.sun.tools.javac.comp;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer;
import javax.tools.JavaFileObject;
@ -284,6 +285,8 @@ public class TypeEnter implements Completer {
Env<AttrContext> env;
ImportFilter staticImportFilter;
ImportFilter typeImportFilter;
BiConsumer<JCImport, CompletionFailure> cfHandler =
(imp, cf) -> chk.completionError(imp.pos(), cf);
@Override
protected void doRunPhase(Env<AttrContext> env) {
@ -327,7 +330,7 @@ public class TypeEnter implements Completer {
PackageSymbol javaLang = syms.enterPackage(names.java_lang);
if (javaLang.members().isEmpty() && !javaLang.exists())
throw new FatalError(diags.fragment("fatal.err.no.java.lang"));
importAll(tree.pos, javaLang, env);
importAll(make.at(tree.pos()).Import(make.QualIdent(javaLang), false), javaLang, env);
// Process the package def and all import clauses.
if (tree.getPackage() != null)
@ -378,13 +381,13 @@ public class TypeEnter implements Completer {
// Import on demand.
chk.checkCanonical(imp.selected);
if (tree.staticImport)
importStaticAll(tree.pos, p, env);
importStaticAll(tree, p, env);
else
importAll(tree.pos, p, env);
importAll(tree, p, env);
} else {
// Named type import.
if (tree.staticImport) {
importNamedStatic(tree.pos(), p, name, localEnv, tree);
importNamedStatic(tree, p, name, localEnv);
chk.checkCanonical(imp.selected);
} else {
TypeSymbol c = attribImportType(imp, localEnv).tsym;
@ -411,51 +414,50 @@ public class TypeEnter implements Completer {
}
/** Import all classes of a class or package on demand.
* @param pos Position to be used for error reporting.
* @param imp The import that is being handled.
* @param tsym The class or package the members of which are imported.
* @param env The env in which the imported classes will be entered.
*/
private void importAll(int pos,
private void importAll(JCImport imp,
final TypeSymbol tsym,
Env<AttrContext> env) {
env.toplevel.starImportScope.importAll(types, tsym.members(), typeImportFilter, false);
env.toplevel.starImportScope.importAll(types, tsym.members(), typeImportFilter, imp, cfHandler);
}
/** Import all static members of a class or package on demand.
* @param pos Position to be used for error reporting.
* @param imp The import that is being handled.
* @param tsym The class or package the members of which are imported.
* @param env The env in which the imported classes will be entered.
*/
private void importStaticAll(int pos,
private void importStaticAll(JCImport imp,
final TypeSymbol tsym,
Env<AttrContext> env) {
final StarImportScope toScope = env.toplevel.starImportScope;
final TypeSymbol origin = tsym;
toScope.importAll(types, origin.members(), staticImportFilter, true);
toScope.importAll(types, origin.members(), staticImportFilter, imp, cfHandler);
}
/** Import statics types of a given name. Non-types are handled in Attr.
* @param pos Position to be used for error reporting.
* @param imp The import that is being handled.
* @param tsym The class from which the name is imported.
* @param name The (simple) name being imported.
* @param env The environment containing the named import
* scope to add to.
*/
private void importNamedStatic(final DiagnosticPosition pos,
private void importNamedStatic(final JCImport imp,
final TypeSymbol tsym,
final Name name,
final Env<AttrContext> env,
final JCImport imp) {
final Env<AttrContext> env) {
if (tsym.kind != TYP) {
log.error(DiagnosticFlag.RECOVERABLE, pos, "static.imp.only.classes.and.interfaces");
log.error(DiagnosticFlag.RECOVERABLE, imp.pos(), "static.imp.only.classes.and.interfaces");
return;
}
final NamedImportScope toScope = env.toplevel.namedImportScope;
final Scope originMembers = tsym.members();
imp.importScope = toScope.importByName(types, originMembers, name, staticImportFilter);
imp.importScope = toScope.importByName(types, originMembers, name, staticImportFilter, imp, cfHandler);
}
/** Import given class.

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2015, 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 8131915
* @summary Verify that CompletionFailure thrown during listing of import content is handled properly.
* @library /tools/lib
*/
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class CompletionFailureDuringImport {
public static void main(String... args) throws Exception {
new CompletionFailureDuringImport().run();
}
ToolBox tb = new ToolBox();
void run() throws Exception {
tb.new JavacTask()
.outdir(".")
.sources("package p; public class Super { public static final int I = 0; }",
"package p; public class Sub extends Super { }")
.run()
.writeAll();
Files.delete(Paths.get(".", "p", "Super.class"));
doTest("import static p.Sub.*;",
"",
"Test.java:1:1: compiler.err.cant.access: p.Super, (compiler.misc.class.file.not.found: p.Super)",
"1 error");
doTest("import static p.Sub.I;",
"",
"Test.java:1:1: compiler.err.cant.access: p.Super, (compiler.misc.class.file.not.found: p.Super)",
"1 error");
doTest("import static p.Sub.*;",
"int i = I;",
"Test.java:1:1: compiler.err.cant.access: p.Super, (compiler.misc.class.file.not.found: p.Super)",
"Test.java:1:52: compiler.err.cant.resolve.location: kindname.variable, I, , , (compiler.misc.location: kindname.class, Test, null)",
"2 errors");
doTest("import static p.Sub.I;",
"int i = I;",
"Test.java:1:1: compiler.err.cant.access: p.Super, (compiler.misc.class.file.not.found: p.Super)",
"Test.java:1:52: compiler.err.cant.resolve.location: kindname.variable, I, , , (compiler.misc.location: kindname.class, Test, null)",
"2 errors");
}
void doTest(String importText, String useText, String... expectedOutput) {
List<String> log = tb.new JavacTask()
.classpath(".")
.sources(importText + " public class Test { " + useText + " }")
.options("-XDrawDiagnostics")
.run(ToolBox.Expect.FAIL)
.writeAll()
.getOutputLines(ToolBox.OutputKind.DIRECT);
if (!log.equals(Arrays.asList(expectedOutput))) {
throw new AssertionError("Unexpected output: " + log);
}
}
}

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 7004029
* @bug 7004029 8131915
* @summary Ensure Scope impl can cope with hash collisions
* @library /tools/javac/lib
* @modules jdk.compiler/com.sun.tools.javac.api
@ -37,6 +37,7 @@
import java.lang.reflect.*;
import java.io.*;
import java.util.function.BiConsumer;
import com.sun.source.util.Trees;
import com.sun.tools.javac.api.JavacTrees;
@ -45,6 +46,8 @@ import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Scope.*;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.tree.JCTree.JCImport;
import com.sun.tools.javac.tree.TreeMaker;
import static com.sun.tools.javac.code.Kinds.Kind.*;
@ -57,6 +60,7 @@ public class HashCollisionTest {
// set up basic environment for test
Context context = new Context();
JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab
make = TreeMaker.instance(context);
names = Names.instance(context); // Name.Table impls tied to an instance of Names
symtab = Symtab.instance(context);
trees = JavacTrees.instance(context);
@ -127,12 +131,14 @@ public class HashCollisionTest {
return sym.kind == TYP;
}
};
starImportScope.importAll(types, fromScope, typeFilter, false);
BiConsumer<JCImport, CompletionFailure> noCompletionFailure =
(imp, cf) -> { throw new IllegalStateException(); };
starImportScope.importAll(types, fromScope, typeFilter, make.Import(null, false), noCompletionFailure);
dump("imported p", starImportScope);
// 7. Insert the class from 3.
starImportScope.importAll(types, cc.members_field, typeFilter, false);
starImportScope.importAll(types, cc.members_field, typeFilter, make.Import(null, false), noCompletionFailure);
dump("imported ce", starImportScope);
/*
@ -199,6 +205,7 @@ public class HashCollisionTest {
int MAX_TRIES = 100; // max tries to find a hash clash before giving up.
int scopeHashMask;
TreeMaker make;
Names names;
Symtab symtab;
Trees trees;

View File

@ -23,7 +23,7 @@
/*
* @test
* @bug 7004029
* @bug 7004029 8131915
* @summary Basher for star-import scopes
* @modules jdk.compiler/com.sun.tools.javac.code
* jdk.compiler/com.sun.tools.javac.file
@ -39,6 +39,7 @@ import com.sun.tools.javac.code.Scope.StarImportScope;
import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
@ -136,6 +137,7 @@ public class StarImportTest {
log ("setup");
context = new Context();
JavacFileManager.preRegister(context); // required by ClassReader which is required by Symtab
make = TreeMaker.instance(context);
names = Names.instance(context); // Name.Table impls tied to an instance of Names
symtab = Symtab.instance(context);
types = Types.instance(context);
@ -227,7 +229,7 @@ public class StarImportTest {
public boolean accepts(Scope origin, Symbol t) {
return t.kind == TYP;
}
}, false);
}, make.Import(null, false), (i, cf) -> { throw new IllegalStateException(); });
for (Symbol sym : members.getSymbols()) {
starImportModel.enter(sym);
@ -295,6 +297,7 @@ public class StarImportTest {
Context context;
Symtab symtab;
TreeMaker make;
Names names;
Types types;
int nextNameSerial;