diff --git a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java index 5fb9510ae327..01c015f96bc0 100644 --- a/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java +++ b/java/java.completion/src/org/netbeans/modules/java/completion/JavaCompletionTask.java @@ -1968,7 +1968,7 @@ private void insideMemberSelect(Env env) throws IOException { addPackageContent(env, (PackageElement) el, kinds, baseType, insideNew, srcOnly, switchItemAdder); if (results.isEmpty() && ((PackageElement) el).getQualifiedName() == el.getSimpleName()) { // no package content? Check for unimported class - ClassIndex ci = controller.getClasspathInfo().getClassIndex(); + ClassIndex ci = controller.getClassIndex(); if (el.getEnclosedElements().isEmpty() && ci.getPackageNames(el.getSimpleName() + ".", true, EnumSet.allOf(ClassIndex.SearchScope.class)).isEmpty()) { Trees trees = controller.getTrees(); Scope scope = env.getScope(); @@ -3982,7 +3982,7 @@ private void addAllStaticMemberNames(final Env env) { } } ClassIndex.NameKind kind = Utilities.isCaseSensitive() ? ClassIndex.NameKind.PREFIX : ClassIndex.NameKind.CASE_INSENSITIVE_PREFIX; - Iterable declaredSymbols = controller.getClasspathInfo().getClassIndex().getDeclaredSymbols(prefix, kind, EnumSet.allOf(ClassIndex.SearchScope.class)); + Iterable declaredSymbols = controller.getClassIndex().getDeclaredSymbols(prefix, kind, EnumSet.allOf(ClassIndex.SearchScope.class)); for (Symbols symbols : declaredSymbols) { if (Utilities.isExcluded(symbols.getEnclosingType().getQualifiedName()) || excludeHandles != null && excludeHandles.contains(symbols.getEnclosingType()) @@ -4462,7 +4462,7 @@ private void addPackages(Env env, String fqnPrefix, boolean srcOnly) { ModuleElement moduleElement = el != null ? controller.getElements().getModuleOf(el) : null; Set seenPkgs = new HashSet<>(); EnumSet scope = srcOnly ? EnumSet.of(ClassIndex.SearchScope.SOURCE) : EnumSet.allOf(ClassIndex.SearchScope.class); - for (String pkgName : env.getController().getClasspathInfo().getClassIndex().getPackageNames(fqnPrefix, false, scope)) { + for (String pkgName : env.getController().getClassIndex().getPackageNames(fqnPrefix, false, scope)) { if (startsWith(env, pkgName, prefix) && !Utilities.isExcluded(pkgName + ".") && (moduleElement != null ? elements.getPackageElement(moduleElement, pkgName) : elements.getPackageElement(pkgName)) != null) { //NOI18N if (fqnPrefix != null) { @@ -4606,7 +4606,7 @@ private void addAllTypes(Env env, EnumSet kinds) { } } if (!kinds.contains(ElementKind.CLASS) && !kinds.contains(ElementKind.INTERFACE)) { - Set> declaredTypes = controller.getClasspathInfo().getClassIndex().getDeclaredTypes(EMPTY, ClassIndex.NameKind.PREFIX, EnumSet.allOf(ClassIndex.SearchScope.class)); + Set> declaredTypes = controller.getClassIndex().getDeclaredTypes(EMPTY, ClassIndex.NameKind.PREFIX, EnumSet.allOf(ClassIndex.SearchScope.class)); Map> removed = new HashMap<>(declaredTypes.size()); Set doNotRemove = new HashSet<>(); for (ElementHandle name : declaredTypes) { @@ -4648,7 +4648,7 @@ private void addAllTypes(Env env, EnumSet kinds) { ? Utilities.isCaseSensitive() ? ClassIndex.NameKind.CAMEL_CASE : ClassIndex.NameKind.CAMEL_CASE_INSENSITIVE : subwordsPattern != null ? ClassIndex.NameKind.REGEXP : Utilities.isCaseSensitive() ? ClassIndex.NameKind.PREFIX : ClassIndex.NameKind.CASE_INSENSITIVE_PREFIX; - Set> declaredTypes = controller.getClasspathInfo().getClassIndex().getDeclaredTypes(subwordsPattern != null ? subwordsPattern : prefix != null ? prefix : EMPTY, kind, EnumSet.allOf(ClassIndex.SearchScope.class)); + Set> declaredTypes = controller.getClassIndex().getDeclaredTypes(subwordsPattern != null ? subwordsPattern : prefix != null ? prefix : EMPTY, kind, EnumSet.allOf(ClassIndex.SearchScope.class)); results.ensureCapacity(results.size() + declaredTypes.size()); for (ElementHandle name : declaredTypes) { if (!kinds.contains(name.getKind()) || excludeHandles != null && excludeHandles.contains(name) || isAnnonInner(name)) { @@ -4701,7 +4701,7 @@ private List getSubtypesOf(Env env, DeclaredType baseType) throws ? Utilities.isCaseSensitive() ? ClassIndex.NameKind.CAMEL_CASE : ClassIndex.NameKind.CAMEL_CASE_INSENSITIVE : subwordsPattern != null ? ClassIndex.NameKind.REGEXP : Utilities.isCaseSensitive() ? ClassIndex.NameKind.PREFIX : ClassIndex.NameKind.CASE_INSENSITIVE_PREFIX; - for (ElementHandle handle : controller.getClasspathInfo().getClassIndex().getDeclaredTypes(subwordsPattern != null ? subwordsPattern : prefix, kind, EnumSet.allOf(ClassIndex.SearchScope.class))) { + for (ElementHandle handle : controller.getClassIndex().getDeclaredTypes(subwordsPattern != null ? subwordsPattern : prefix, kind, EnumSet.allOf(ClassIndex.SearchScope.class))) { TypeElement te = handle.resolve(controller); if (te != null && trees.isAccessible(scope, te) && types.isSubtype(types.getDeclaredType(te), baseType)) { subtypes.add(types.getDeclaredType(te)); @@ -4711,7 +4711,7 @@ private List getSubtypesOf(Env env, DeclaredType baseType) throws HashSet elems = new HashSet<>(); LinkedList bases = new LinkedList<>(); bases.add(baseType); - ClassIndex index = controller.getClasspathInfo().getClassIndex(); + ClassIndex index = controller.getClassIndex(); while (!bases.isEmpty()) { DeclaredType head = bases.remove(); TypeElement elem = (TypeElement) head.asElement(); @@ -5769,7 +5769,7 @@ private Set getSmartTypes(Env env) throws IOException { Types types = controller.getTypes(); TypeElement te = (TypeElement) ((DeclaredType) err).asElement(); if (te.getQualifiedName() == te.getSimpleName()) { - ClassIndex ci = controller.getClasspathInfo().getClassIndex(); + ClassIndex ci = controller.getClassIndex(); for (ElementHandle eh : ci.getDeclaredTypes(te.getSimpleName().toString(), ClassIndex.NameKind.SIMPLE_NAME, EnumSet.allOf(ClassIndex.SearchScope.class))) { te = eh.resolve(controller); if (te != null) { diff --git a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java index 206290be7d62..cae6ee844f8f 100644 --- a/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java +++ b/java/java.editor/src/org/netbeans/modules/editor/java/JavaCompletionItem.java @@ -4770,7 +4770,7 @@ private static TypeMirror typeToImport(CompilationInfo info, TreePath tp, TypeMi if (tp.getLeaf().getKind() == Tree.Kind.MEMBER_SELECT) { MemberSelectTree mst = (MemberSelectTree) tp.getLeaf(); if (mst.getExpression().getKind() == Tree.Kind.IDENTIFIER) { - ClassIndex index = info.getClasspathInfo().getClassIndex(); + ClassIndex index = info.getClassIndex(); Types types = info.getTypes(); Trees trees = info.getTrees(); Scope scope = trees.getScope(tp); diff --git a/java/java.editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java b/java/java.editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java index 28f0cdceb135..da73feb9681a 100644 --- a/java/java.editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java +++ b/java/java.editor/src/org/netbeans/modules/java/editor/imports/ComputeImports.java @@ -104,7 +104,6 @@ public ComputeImports(final CompilationInfo info) { } private final CompilationInfo info; - private CompilationInfo allInfo; private final PreferenceChangeListener pcl = new PreferenceChangeListener() { @Override @@ -165,42 +164,7 @@ private ComputeImports computeCandidatesEx(Set forcedUnresolved) { if (cache != null) { return cache; } - boolean modules = false; - - if (info.getSourceVersion().compareTo(SourceVersion.RELEASE_9) <= 0) { - if (info.getClasspathInfo().getClassPath(ClasspathInfo.PathKind.SOURCE).findResource("module-info.java") != null) { - modules = true; - } - } - - if (modules) { - ClasspathInfo cpInfo = info.getClasspathInfo(); - ClasspathInfo extraInfo = ClasspathInfo.create( - ClassPathSupport.createProxyClassPath( - cpInfo.getClassPath(ClasspathInfo.PathKind.BOOT), - cpInfo.getClassPath(ClasspathInfo.PathKind.MODULE_BOOT)), - ClassPathSupport.createProxyClassPath( - cpInfo.getClassPath(ClasspathInfo.PathKind.COMPILE), - cpInfo.getClassPath(ClasspathInfo.PathKind.MODULE_COMPILE), - cpInfo.getClassPath(ClasspathInfo.PathKind.MODULE_CLASS)), - cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE)); - JavaSource src = JavaSource.create(extraInfo, info.getSnapshot().getSource().getFileObject()); - try { - src.runUserActionTask(new Task() { - @Override - public void run(CompilationController parameter) throws Exception { - allInfo = parameter; - parameter.toPhase(JavaSource.Phase.RESOLVED); - doComputeCandidates(forcedUnresolved); - } - }, true); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - } - } else { - allInfo = info; - doComputeCandidates(forcedUnresolved); - } + doComputeCandidates(forcedUnresolved); info.putCachedValue(IMPORT_CANDIDATES_KEY, this, CacheClearPolicy.ON_CHANGE); return this; } @@ -225,7 +189,6 @@ private synchronized void setVisitor(TreeVisitorImpl visitor) { private void doComputeCandidates(Set forcedUnresolved) { final CompilationUnitTree cut = info.getCompilationUnit(); - ClasspathInfo cpInfo = allInfo.getClasspathInfo(); final TreeVisitorImpl v = new TreeVisitorImpl(info); setVisitor(v); try { @@ -249,7 +212,7 @@ private void doComputeCandidates(Set forcedUnresolved) { return; List classes = new ArrayList(); - Set> typeNames = cpInfo.getClassIndex().getDeclaredTypes(unresolved, NameKind.SIMPLE_NAME,EnumSet.allOf(ClassIndex.SearchScope.class)); + Set> typeNames = info.getClassIndex().getDeclaredTypes(unresolved, NameKind.SIMPLE_NAME,EnumSet.allOf(ClassIndex.SearchScope.class)); if (typeNames == null) { //Canceled return; @@ -257,7 +220,7 @@ private void doComputeCandidates(Set forcedUnresolved) { for (ElementHandle typeName : typeNames) { if (isCancelled()) return; - TypeElement te = typeName.resolve(allInfo); + TypeElement te = typeName.resolve(info); if (te == null) { Logger.getLogger(ComputeImports.class.getName()).log(Level.INFO, "Cannot resolve type element \"" + typeName + "\"."); @@ -272,7 +235,7 @@ private void doComputeCandidates(Set forcedUnresolved) { } if (unresolvedNonTypes.contains(unresolved)) { - Iterable simpleNames = cpInfo.getClassIndex().getDeclaredSymbols(unresolved, NameKind.SIMPLE_NAME,EnumSet.allOf(ClassIndex.SearchScope.class)); + Iterable simpleNames = info.getClassIndex().getDeclaredSymbols(unresolved, NameKind.SIMPLE_NAME,EnumSet.allOf(ClassIndex.SearchScope.class)); if (simpleNames == null) { //Canceled: @@ -283,7 +246,7 @@ private void doComputeCandidates(Set forcedUnresolved) { if (isCancelled()) return; - final TypeElement te = p.getEnclosingType().resolve(allInfo); + final TypeElement te = p.getEnclosingType().resolve(info); final Set idents = p.getSymbols(); if (te != null) { for (Element ne : te.getEnclosedElements()) { @@ -312,7 +275,7 @@ private void doComputeCandidates(Set forcedUnresolved) { possibleMethodFQNs.clear(); for (Hint hint: v.hints) { - wasChanged |= hint.filter(allInfo, this); + wasChanged |= hint.filter(info, this); } } diff --git a/java/java.source.base/src/META-INF/upgrade/getClassIndex.hint b/java/java.source.base/src/META-INF/upgrade/getClassIndex.hint new file mode 100644 index 000000000000..0dee7874fe93 --- /dev/null +++ b/java/java.source.base/src/META-INF/upgrade/getClassIndex.hint @@ -0,0 +1,6 @@ +"Please avoid ClasspathInfo.getClassIndex() wherever possible": +$cpInfo.getClassIndex() :: $cpInfo instanceof org.netbeans.api.java.source.ClasspathInfo +=> +"Use CompilationInfo.getClassIndex()": +$info.getClassIndex() :: matchesWithBind($cpInfo, "$info.getClasspathInfo()") && $info instanceof org.netbeans.api.java.source.CompilationInfo +;; diff --git a/java/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java b/java/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java index c80f9cb83d86..135a52a00af9 100644 --- a/java/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java +++ b/java/java.source.base/src/org/netbeans/api/java/source/ClasspathInfo.java @@ -37,6 +37,7 @@ import java.util.logging.Logger; import java.util.logging.Level; import java.util.stream.Collectors; +import javax.lang.model.SourceVersion; import javax.swing.event.ChangeListener; import javax.swing.text.Document; import javax.tools.JavaFileManager; @@ -61,6 +62,7 @@ import org.netbeans.modules.parsing.impl.indexing.PathRegistry; import org.netbeans.spi.java.classpath.ClassPathFactory; import org.netbeans.spi.java.classpath.ClassPathImplementation; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.BaseUtilities; diff --git a/java/java.source.base/src/org/netbeans/api/java/source/CompilationInfo.java b/java/java.source.base/src/org/netbeans/api/java/source/CompilationInfo.java index 938a266081cc..9ca8d7a70f29 100644 --- a/java/java.source.base/src/org/netbeans/api/java/source/CompilationInfo.java +++ b/java/java.source.base/src/org/netbeans/api/java/source/CompilationInfo.java @@ -493,6 +493,15 @@ ModuleElement getModule() { } } + /** + * TODO. + * only valid after toPhase(ELEMENTS_RESOLVED) + * @return + */ + public ClassIndex getClassIndex() { + return impl.getClassIndex(); + } + /**Constants to specify when a valued cached by {@link #putCachedValue(java.lang.Object, java.lang.Object, org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy)} * should be evicted from the cache. * diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/CompilationInfoImpl.java b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/CompilationInfoImpl.java index 1269c6c7cbcf..82b9257376fb 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/CompilationInfoImpl.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/CompilationInfoImpl.java @@ -24,12 +24,24 @@ import com.sun.tools.javac.api.ClientCodeWrapper.Trusted; import com.sun.tools.javac.api.DiagnosticFormatter; import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol.ModuleSymbol; +import com.sun.tools.javac.code.Symtab; +import com.sun.tools.javac.comp.Modules; +import com.sun.tools.javac.file.Locations; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.Log; import java.io.IOException; import java.lang.ref.Reference; import java.lang.ref.SoftReference; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -42,6 +54,7 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import java.util.stream.Collectors; @@ -50,10 +63,13 @@ import javax.tools.Diagnostic; import javax.tools.Diagnostic.Kind; import javax.tools.DiagnosticListener; +import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.annotations.common.NullAllowed; import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.source.ClassIndex; import org.netbeans.api.java.source.ClasspathInfo; import org.netbeans.api.java.source.CompilationInfo; import org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy; @@ -63,7 +79,11 @@ import org.netbeans.modules.java.source.indexing.JavaIndex; import org.netbeans.modules.parsing.api.Snapshot; import org.netbeans.modules.parsing.api.Source; +import org.netbeans.modules.parsing.impl.indexing.CacheFolder; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.filesystems.URLMapper; import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.Pair; @@ -98,6 +118,7 @@ public final class CompilationInfoImpl { private Map ide2javacFileObject; private Map fileObject2Snapshot; private boolean incomplete; + private ClassIndex classIndex; /** * Creates a new CompilationInfoImpl for given source file @@ -578,6 +599,80 @@ public void markIncomplete() { this.incomplete = true; } + public ClassIndex getClassIndex() { + if (classIndex == null) { + if (this.phase.compareTo (JavaSource.Phase.ELEMENTS_RESOLVED) < 0) + throw new IllegalStateException("Cannot call getCompilationUnit() if current phase < JavaSource.Phase.ELEMENTS_RESOLVED. You must call toPhase(Phase.ELEMENTS_RESOLVED) first.");//NOI18N + Context context = javacTask.getContext(); + Modules modules = Modules.instance(context); + Symtab syms = Symtab.instance(context); + if (modules.getDefaultModule() == syms.noModule) { + classIndex = cpInfo.getClassIndex(); + } else { + ModuleSymbol mainModule; + if (this.jfo == null) { + mainModule = modules.getDefaultModule(); + } else { + mainModule = ((JCTree.JCCompilationUnit) compilationUnit).modle; + } + Set allModules = new HashSet<>(); + mainModule.visiblePackages.values().forEach(ps -> allModules.add(ps.modle)); + allModules.remove(mainModule); + allModules.remove(syms.unnamedModule); + List bootRoots = new ArrayList<>(); + List compileRoots = new ArrayList<>(); + if (mainModule.requires.stream().anyMatch(rd -> rd.module == syms.unnamedModule)) { + cpInfo.getClassPath(ClasspathInfo.PathKind.MODULE_CLASS) + .entries() + .forEach(e -> compileRoots.add(e.getURL())); + } + for (ModuleSymbol ms : allModules) { + //TODO: patch module + List targetRoots = (ms.flags() & Flags.SYSTEM_MODULE) != 0 ? bootRoots : compileRoots; + List candidateRoots = new ArrayList<>(); + if (ms.classLocation instanceof ModuleLocation moduleLoc) { + moduleLoc.getModuleRoots() + .forEach(candidateRoots::add); + } else if ((ms.flags() & Flags.SYSTEM_MODULE) != 0) { + //this is meant for the implicit/hardcoded --release option when source level check fails: + String augmentedModuleName = "/" + ms.name.toString() + "/"; + cpInfo.getClassPath(ClasspathInfo.PathKind.MODULE_BOOT) + .entries() + .stream() + .map(ClassPath.Entry::getURL) + .filter(url -> url.getPath().endsWith(augmentedModuleName)) + .forEach(candidateRoots::add); + } else { + throw new InternalError("Unexpected class location: " + ms.classLocation.getClass().getName()); + } + if (ms.patchLocation instanceof ModuleLocation patchLoc) { + patchLoc.getModuleRoots() + .forEach(candidateRoots::add); + } else if (ms.patchLocation != null) { + throw new InternalError("Unexpected patch location: " + ms.patchLocation.getClass().getName()); + } + for (URL root : candidateRoots) { + //TODO: this is not really tested, but is needed: + URL sourceRoot = CacheFolder.getSourceRootForDataFolder(URLMapper.findFileObject(root)); + if (sourceRoot != null) { + targetRoots.add(sourceRoot); + } else { + targetRoots.add(root); + } + } + } + //TODO: use an accessor, and avoid creating the CPInfo: + ClasspathInfo adjustedCPInfo = ClasspathInfo.create( + ClassPathSupport.createClassPath(bootRoots.toArray(URL[]::new)), + ClassPathSupport.createClassPath(compileRoots.toArray(URL[]::new)), + cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE)); + + classIndex = adjustedCPInfo.getClassIndex(); + } + } + return classIndex; + } + // Innerclasses ------------------------------------------------------------ @Trusted public static class DiagnosticListenerImpl implements DiagnosticListener { diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java index 8f2a89d11452..5464292a4f95 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java @@ -159,7 +159,11 @@ public int isSupportedOption(String option) { @Override public boolean handleOption(String head, Iterator tail) { - if (JavacParser.OPTION_PATCH_MODULE.equals(head)) { + if (head.startsWith(JavacParser.OPTION_PATCH_MODULE)) { + if (head.length() > JavacParser.OPTION_PATCH_MODULE.length() && + head.charAt(JavacParser.OPTION_PATCH_MODULE.length()) == '=') { + tail = List.of(head.substring(JavacParser.OPTION_PATCH_MODULE.length() + 1)).iterator(); + } final Pair> modulePatches = FileObjects.parseModulePatches(tail); if (modulePatches != null) { addModulePatches(modulePatches.first(), modulePatches.second()); diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/ProxyFileManager.java b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/ProxyFileManager.java index fffcf8061d50..4dbc7469670e 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/ProxyFileManager.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/ProxyFileManager.java @@ -750,7 +750,7 @@ JavaFileManager[] getFileManagers(@NonNull Location location, @NullAllowed Strin if (TreeLoaderOutputFileManager.OUTPUT_ROOT.equals(hint)) { createTreeLoaderFileManager(); } - if (JavacParser.OPTION_PATCH_MODULE.equals(hint) || (hint != null && hint.startsWith(JavacParser.NB_X_MODULE))) { + if (hint != null && (hint.startsWith(JavacParser.OPTION_PATCH_MODULE) || hint.startsWith(JavacParser.NB_X_MODULE))) { createPatchFileManager(); createModuleSrcFileManager(); } diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/CompilationInfoTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/CompilationInfoTest.java index 0933d86b17f6..f4be1289a2ea 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/CompilationInfoTest.java +++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/CompilationInfoTest.java @@ -21,22 +21,38 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintWriter; import java.text.MessageFormat; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import javax.lang.model.SourceVersion; -import javax.swing.text.BadLocationException; +import javax.lang.model.element.TypeElement; import javax.swing.text.Document; import org.junit.Test; +import org.netbeans.api.editor.mimelookup.MimePath; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.classpath.GlobalPathRegistry; import org.netbeans.api.java.lexer.JavaTokenId; import org.netbeans.api.java.source.CompilationInfo.CacheClearPolicy; import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.api.java.source.SourceUtilsTestUtil.FileDescription; import org.netbeans.api.lexer.Language; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.java.source.BootClassPathUtil; +import org.netbeans.modules.java.source.indexing.JavaBinaryIndexer; +import org.netbeans.spi.editor.mimelookup.MimeDataProvider; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; import org.openide.cookies.EditorCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ServiceProvider; /** * @@ -160,4 +176,222 @@ public void run(CompilationController parameter) throws Exception { } }, true); } + + public void testGetClassIndex1() throws Exception { + clearWorkDir(); + + File work = getWorkDir(); + FileObject workFO = FileUtil.toFileObject(work); + + assertNotNull(workFO); + + FileObject module1 = workFO.createFolder("module1"); + FileObject module1Src = module1.createFolder("src"); + FileObject module1Classes = module1.createFolder("classes"); + + SourceUtilsTestUtil.writeFiles(module1Src, + new FileDescription("module-info.java", + """ + module module1 { + exports api; + } + """), + new FileDescription("api/Api.java", + """ + package api; + public class Api { + } + """)); + SourceUtilsTestUtil.compile(module1Src, module1Classes, "21"); + + FileObject module2 = workFO.createFolder("module2"); + FileObject module2Src = module2.createFolder("src"); + FileObject module2Classes = module2.createFolder("classes"); + + SourceUtilsTestUtil.writeFiles(module2Src, + new FileDescription("module-info.java", + """ + module module2 { + requires transitive module1; + exports api2; + } + """), + new FileDescription("api2/Api2.java", + """ + package api2; + public class Api2 { + } + """)); + SourceUtilsTestUtil.compile(module2Src, module2Classes, "21", "--module-path", FileUtil.toFile(module1Classes).getAbsolutePath()); + + FileObject module3 = workFO.createFolder("module3"); + FileObject module3Src = module3.createFolder("src"); + FileObject module3Classes = module3.createFolder("classes"); + + SourceUtilsTestUtil.writeFiles(module3Src, + new FileDescription("module-info.java", + """ + module module3 { + exports api3; + } + """), + new FileDescription("api3/Api3.java", + """ + package api3; + public class Api3 { + } + """)); + SourceUtilsTestUtil.compile(module3Src, module3Classes, "21"); + + FileObject patch = workFO.createFolder("patch"); + FileObject patchSrc = patch.createFolder("src"); + FileObject patchClasses = patch.createFolder("classes"); + + SourceUtilsTestUtil.writeFiles(patchSrc, + new FileDescription("api/AddedPatch.java", + """ + package api; + public class AddedPatch { + } + """)); + SourceUtilsTestUtil.compile(patchSrc, patchClasses, "21"); + + FileObject src = workFO.createFolder("src"); + FileObject classes = workFO.createFolder("classes"); + + SourceUtilsTestUtil.writeFiles(src, + new FileDescription("module-info.java", + """ + module test { + requires module2; + } + """), + new FileDescription("test/Test.java", + """ + package test; + public class Test { + } + """)); + + SourceUtilsTestUtil.prepareTest(src, classes, workFO.createFolder("cache"), new FileObject[] { + module1Classes, module2Classes, module3Classes, patchClasses //to make indexing work ( :-( ) - TODO: can be made better? + }, new FileObject[] { + module1Classes, module2Classes, module3Classes + }); + SourceUtilsTestUtil.setSourceLevel(src, "21"); + SourceUtilsTestUtil.compileRecursively(src); + + ClassPath srcPath = ClassPathSupport.createClassPath(src); + FileObject testFile = src.getFileObject("test/Test.java"); + + ClasspathInfo cpInfo; + + cpInfo = new ClasspathInfo.Builder(BootClassPathUtil.getBootClassPath()) //bootclasspath: prevent source level downgrade + .setModuleBootPath(BootClassPathUtil.getModuleBootPath()) + .setModuleCompilePath(ClassPathSupport.createClassPath(module1Classes.toURL(), module2Classes.toURL(), module3Classes.toURL())) + .setSourcePath(srcPath).build(); + GlobalPathRegistry.getDefault().register(ClassPath.SOURCE, new ClassPath[]{ + srcPath}); + Set found; + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of("api.Api", "api2.Api2"), found); + found = getDeclaredTypes(cpInfo, testFile, "String"); + assertTrue(found.contains("java.lang.String")); + assertTrue(found.contains("java.lang.StringBuilder")); + + //module classpath: + cpInfo = new ClasspathInfo.Builder(ClassPath.EMPTY) //attempt to cause source level downgrade + .setModuleBootPath(BootClassPathUtil.getModuleBootPath()) + .setModuleCompilePath(ClassPathSupport.createClassPath(module1Classes.toURL(), module2Classes.toURL(), module3Classes.toURL())) + .setModuleClassPath(ClassPathSupport.createClassPath(module3Classes.toURL())) + .setSourcePath(srcPath).build(); + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of("api.Api", "api2.Api2"), found); + found = getDeclaredTypes(cpInfo, testFile, "String"); + assertTrue(found.contains("java.lang.String")); + assertTrue(found.contains("java.lang.StringBuilder")); + found = getDeclaredTypes(cpInfo, testFile, "List"); + assertTrue(found.contains("java.util.List")); + assertFalse(found.contains("java.awt.List")); + + //module classpath: + cpInfo = new ClasspathInfo.Builder(BootClassPathUtil.getBootClassPath()) //bootclasspath: prevent source level downgrade + .setModuleBootPath(BootClassPathUtil.getModuleBootPath()) + .setModuleCompilePath(ClassPathSupport.createClassPath(module1Classes.toURL(), module2Classes.toURL(), module3Classes.toURL())) + .setModuleClassPath(ClassPathSupport.createClassPath(module3Classes.toURL())) + .setSourcePath(srcPath).build(); + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of("api.Api", "api2.Api2"), found); + SourceUtilsTestUtil.setCompilerOptions(src, List.of("--add-reads=test=ALL-UNNAMED")); + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of("api.Api", "api2.Api2", "api3.Api3"), found); + SourceUtilsTestUtil.setCompilerOptions(src, null); + + //unnamed module: + src.getFileObject("module-info.java").delete(); + SourceUtilsTestUtil.compileRecursively(src); + SourceUtilsTestUtil.setCompilerOptions(src, List.of("--limit-modules=module2", "--add-modules=module2")); + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of("api.Api", "api2.Api2"), found); + SourceUtilsTestUtil.setCompilerOptions(src, List.of("--limit-modules=module3", "--add-modules=module3")); + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of("api3.Api3"), found); + SourceUtilsTestUtil.setCompilerOptions(src, null); + + //patch-module: + SourceUtilsTestUtil.setCompilerOptions(src, List.of("--add-modules=module1", "--patch-module=module1=" + FileUtil.toFile(patchClasses).getAbsolutePath())); + found = getDeclaredTypes(cpInfo, testFile, "AddedPat"); + assertEquals(Set.of("api.AddedPatch"), found); + + //source level == 8: + SourceUtilsTestUtil.setSourceLevel(src, "8"); + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of(), found); + cpInfo = new ClasspathInfo.Builder(BootClassPathUtil.getBootClassPath()) + .setClassPath(ClassPathSupport.createClassPath(module3Classes.toURL())) + .setSourcePath(srcPath).build(); + found = getDeclaredTypes(cpInfo, testFile, "Api"); + assertEquals(Set.of("api3.Api3"), found); + } + + private Set getDeclaredTypes(ClasspathInfo cpInfo, FileObject file, String prefix) throws Exception { + //force reparse: + byte[] data = file.asBytes(); + try (OutputStream out = file.getOutputStream()) { + out.write(data); + } + + Set found = new HashSet<>(); + + JavaSource.create(cpInfo, file) + .runWhenScanFinished(cc -> { + cc.toPhase(Phase.ELEMENTS_RESOLVED); + ClassIndex ci = cc.getClassIndex(); + Set> types = ci.getDeclaredTypes(prefix, ClassIndex.NameKind.PREFIX, EnumSet.of(ClassIndex.SearchScope.DEPENDENCIES)); + types.stream() + .map(eh -> eh.getBinaryName()) + .forEach(found::add); + }, true).get(); + + found.remove("com.sun.tools.javac.util.DefinedBy$Api"); //TODO: workaround + return found; + } + + @ServiceProvider(service=MimeDataProvider.class) + public static final class JavaBinaryIndexerProvider implements MimeDataProvider { + + private final Lookup lookup = Lookups.fixed(new JavaBinaryIndexer.Factory()); + + public Lookup getLookup(MimePath mimePath) { + if (mimePath.getPath().isEmpty()) { + return lookup; + } + return Lookup.EMPTY; + } + + } + + static { + System.setProperty("SourcePath.no.source.filter", "true"); + } } diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java index 0d369a34024e..f5e33b7ffd44 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java +++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/ElementUtilitiesTest.java @@ -57,6 +57,7 @@ import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; +import org.netbeans.api.java.source.SourceUtilsTestUtil.FileDescription; /** * @@ -94,39 +95,12 @@ private void prepareTest(FileDescription... fileNameAndContent) throws Exception SourceUtilsTestUtil.prepareTest(sourceRoot, buildRoot, cache, EMPTY_PATH, modulePathElements); if (fileNameAndContent.length > 0) { - testFO = writeFiles(sourceRoot, fileNameAndContent); + testFO = SourceUtilsTestUtil.writeFiles(sourceRoot, fileNameAndContent); } else { testFO = sourceRoot.createData("Test.java"); } } - private FileObject writeFiles(FileObject src, - FileDescription... fileNameAndContent) throws Exception { - FileObject firstFile = null; - - for (FileDescription fileDescription : fileNameAndContent) { - FileObject f = writeFile(src, - fileDescription.path(), - fileDescription.content()); - - if (firstFile == null) { - firstFile = f; - } - } - - return firstFile; - } - - private FileObject writeFile(FileObject root, - String path, - String content) throws Exception { - FileObject file = FileUtil.createData(root, path); - - TestUtilities.copyStringToFile(FileUtil.toFile(file), content); - - return file; - } - public void testGetImplementationOfAndOverriden() throws Exception { prepareTest(); SourceUtilsTestUtil.setSourceLevel(testFO, "8"); @@ -857,7 +831,7 @@ public void testTransitivelyExportedPackages() throws Exception { FileObject module1Src = module1.createFolder("src"); FileObject module1Classes = module1.createFolder("classes"); - writeFiles(module1Src, + SourceUtilsTestUtil.writeFiles(module1Src, new FileDescription("module-info.java", """ module module1 { @@ -890,13 +864,13 @@ public class Api1c { public class Impl1 { } """)); - compile(module1Src, module1Classes, "24"); + SourceUtilsTestUtil.compile(module1Src, module1Classes, "24"); FileObject module2 = workFO.createFolder("module2"); FileObject module2Src = module2.createFolder("src"); FileObject module2Classes = module2.createFolder("classes"); - writeFiles(module2Src, + SourceUtilsTestUtil.writeFiles(module2Src, new FileDescription("module-info.java", """ module module2 { @@ -930,7 +904,7 @@ public class Api2c { public class Impl2 { } """)); - compile(module2Src, module2Classes, "24", "--module-path", FileUtil.toFile(module1Classes).getAbsolutePath()); + SourceUtilsTestUtil.compile(module2Src, module2Classes, "24", "--module-path", FileUtil.toFile(module1Classes).getAbsolutePath()); modulePathElements = new FileObject[] { module1Classes, @@ -963,30 +937,4 @@ public void run(CompilationController controller) throws IOException { } }, true); } - - private void compile(FileObject src, FileObject classes, String sourceLevel, String... extraOpts) throws IOException { - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { - List sources = new ArrayList<>(); - - for (Enumeration en = src.getChildren(true); en.hasMoreElements(); ) { - FileObject c = en.nextElement(); - - if (c.isData() && "text/x-java".equals(c.getMIMEType())) { - sources.add(FileUtil.toFile(c)); - } - } - - Iterable sourceFileObjects = fm.getJavaFileObjectsFromFiles(sources); - List options = new ArrayList<>(); - - options.addAll(List.of("--release", sourceLevel, "-d")); - options.addAll(List.of(FileUtil.toFile(classes).getAbsolutePath())); - options.addAll(List.of(extraOpts)); - - assertTrue(compiler.getTask(null, fm, null, options, null, sourceFileObjects).call()); - } - } - - private record FileDescription(String path, String content) {} } diff --git a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java index 9a1c9220283c..233ed5b4e211 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java +++ b/java/java.source.base/test/unit/src/org/netbeans/api/java/source/SourceUtilsTestUtil.java @@ -30,6 +30,10 @@ import java.util.stream.Collectors; import javax.swing.event.ChangeListener; import javax.swing.text.Document; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; import org.junit.Assert; import org.netbeans.api.editor.mimelookup.MimePath; import org.netbeans.api.java.classpath.ClassPath; @@ -72,6 +76,9 @@ import org.openide.util.lookup.ServiceProvider; import org.xml.sax.SAXException; +import static junit.framework.TestCase.assertTrue; +import org.netbeans.modules.java.source.indexing.JavaBinaryIndexer; + /** * * @author Jan Lahoda @@ -447,6 +454,60 @@ public void run( CompilationController info ) { } + public static void compile(FileObject src, FileObject classes, String sourceLevel, String... extraOpts) throws IOException { + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + try (StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null)) { + List sources = new ArrayList<>(); + + for (Enumeration en = src.getChildren(true); en.hasMoreElements(); ) { + FileObject c = en.nextElement(); + + if (c.isData() && "text/x-java".equals(c.getMIMEType())) { + sources.add(FileUtil.toFile(c)); + } + } + + Iterable sourceFileObjects = fm.getJavaFileObjectsFromFiles(sources); + List options = new ArrayList<>(); + + options.addAll(List.of("--release", sourceLevel, "-d")); + options.addAll(List.of(FileUtil.toFile(classes).getAbsolutePath())); + options.addAll(List.of(extraOpts)); + + assertTrue(compiler.getTask(null, fm, null, options, null, sourceFileObjects).call()); + } + } + + public static FileObject writeFiles(FileObject src, + FileDescription... fileNameAndContent) throws Exception { + FileObject firstFile = null; + + for (FileDescription fileDescription : fileNameAndContent) { + FileObject f = writeFile(src, + fileDescription.path(), + fileDescription.content()); + + if (firstFile == null) { + firstFile = f; + } + } + + return firstFile; + } + + public static FileObject writeFile(FileObject root, + String path, + String content) throws Exception { + FileObject file = FileUtil.createData(root, path); + + TestUtilities.copyStringToFile(FileUtil.toFile(file), content); + + return file; + } + + + public record FileDescription(String path, String content) {} + @ServiceProvider(service=MimeDataProvider.class) public static final class JavacParserProvider implements MimeDataProvider {