diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassDir.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassDir.java index 19a659a715..95061dcff0 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassDir.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassDir.java @@ -16,7 +16,7 @@ package ghidra.util.classfinder; import java.io.File; -import java.util.Set; +import java.util.List; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -33,8 +33,8 @@ class ClassDir { classPackage = new ClassPackage(dir, "", monitor); } - void getClasses(Set set, TaskMonitor monitor) throws CancelledException { - classPackage.getClasses(set, monitor); + void getClasses(List list, TaskMonitor monitor) throws CancelledException { + classPackage.getClasses(list, monitor); } String getDirPath() { diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassJar.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassJar.java index 47ac3d4fdc..9d7171f7e4 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassJar.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassJar.java @@ -62,8 +62,8 @@ class ClassJar implements ClassLocation { } @Override - public void getClasses(Set set, TaskMonitor monitor) { - set.addAll(classes); + public void getClasses(List list, TaskMonitor monitor) { + list.addAll(classes); } private void scanJar(TaskMonitor monitor) throws CancelledException { diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassLocation.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassLocation.java index f4b08c43df..4889922732 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassLocation.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassLocation.java @@ -15,7 +15,7 @@ */ package ghidra.util.classfinder; -import java.util.Set; +import java.util.List; import ghidra.util.exception.CancelledException; import ghidra.util.task.TaskMonitor; @@ -27,5 +27,5 @@ interface ClassLocation { public static final String CLASS_EXT = ".class"; - public void getClasses(Set set, TaskMonitor monitor) throws CancelledException; + public void getClasses(List list, TaskMonitor monitor) throws CancelledException; } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassPackage.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassPackage.java index f32fdc89f9..0383c7d2ec 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassPackage.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassPackage.java @@ -89,15 +89,16 @@ class ClassPackage implements ClassLocation { } @Override - public void getClasses(Set set, TaskMonitor monitor) throws CancelledException { + public void getClasses(List list, TaskMonitor monitor) + throws CancelledException { - set.addAll(classes); + list.addAll(classes); Iterator it = children.iterator(); while (it.hasNext()) { monitor.checkCancelled(); ClassPackage subPkg = it.next(); - subPkg.getClasses(set, monitor); + subPkg.getClasses(list, monitor); } } diff --git a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java index 79a78896d1..011acf42d7 100644 --- a/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java +++ b/Ghidra/Framework/Generic/src/main/java/ghidra/util/classfinder/ClassSearcher.java @@ -30,8 +30,6 @@ import java.util.stream.Collectors; import javax.swing.event.ChangeListener; -import org.apache.commons.collections4.BidiMap; -import org.apache.commons.collections4.bidimap.DualHashBidiMap; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -81,7 +79,7 @@ public class ClassSearcher { private static List> FILTER_CLASSES = Arrays.asList(ExtensionPoint.class); private static Pattern extensionPointSuffixPattern; private static Map> extensionPointSuffixToInfoMap; - private static BidiMap> loadedCache = new DualHashBidiMap<>(); + private static Map> loadedCache = new HashMap<>(); private static Set falsePositiveCache = new HashSet<>(); private static volatile boolean hasSearched; private static volatile boolean isSearching; @@ -187,12 +185,6 @@ public class ClassSearcher { Class c = loadedCache.get(info); if (c == null) { c = loadExtensionPoint(info.path(), info.name()); - ClassFileInfo existing = loadedCache.getKey(c); - if (existing != null) { - log.info( - "Skipping load of class '%s' from '%s'. Already loaded from '%s'." - .formatted(info.name(), info.path(), existing.path())); - } if (c == null) { falsePositiveCache.add(info); continue; @@ -418,8 +410,8 @@ public class ClassSearcher { throws CancelledException { log.info("Searching for classes..."); - Set classDirs = new HashSet<>(); - Set classJars = new HashSet<>(); + List classDirs = new ArrayList<>(); + List classJars = new ArrayList<>(); for (String searchPath : gatherSearchPaths()) { String lcSearchPath = searchPath.toLowerCase(); @@ -441,17 +433,31 @@ public class ClassSearcher { } } - Set classSet = new HashSet<>(); + List classList = new ArrayList<>(); for (ClassDir dir : classDirs) { monitor.checkCancelled(); - dir.getClasses(classSet, monitor); + dir.getClasses(classList, monitor); } for (ClassJar jar : classJars) { monitor.checkCancelled(); - jar.getClasses(classSet, monitor); + jar.getClasses(classList, monitor); } - return classSet.stream() + // We can't load more than one class with the same name, so de-duplicate them + Map uniqueClassMap = new HashMap<>(); + for (ClassFileInfo info : classList) { + ClassFileInfo existing = uniqueClassMap.get(info.name()); + if (existing == null) { + uniqueClassMap.put(info.name(), info); + } + else { + log.info("Ignoring class '%s' from '%s'. Already found at '%s'." + .formatted(info.name(), info.path(), existing.path())); + } + } + + return uniqueClassMap.values() + .stream() .collect(Collectors.groupingBy(ClassFileInfo::suffix, Collectors.toSet())); } diff --git a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java index 87dc7828a6..8a0299d69f 100644 --- a/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java +++ b/Ghidra/Framework/Utility/src/main/java/ghidra/GhidraLauncher.java @@ -165,6 +165,15 @@ public class GhidraLauncher { else { /* Eclipse dev mode */ // Support loading pre-built, jar-based, non-repo extensions in Eclipse dev mode addExtensionJarPaths(classpathList, modules, layout); + + // Eclipse launches the Utility module, so it's already on the classpath. We don't + // want to add it a second time, so remove the one we discovered. + GModule utilityModule = modules.get("Utility"); + if (utilityModule == null) { + throw new IOException("Failed to find the 'Utility' module!"); + } + classpathList.removeIf( + e -> e.startsWith(utilityModule.getModuleRoot().getAbsolutePath())); } // In development mode, jars do not live in module directories. Instead, each jar lives