mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-4608: Improving how we handle loading classes with the same name
This commit is contained in:
parent
72d9d0eeb2
commit
87131b4762
6 changed files with 41 additions and 25 deletions
|
@ -16,7 +16,7 @@
|
||||||
package ghidra.util.classfinder;
|
package ghidra.util.classfinder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -33,8 +33,8 @@ class ClassDir {
|
||||||
classPackage = new ClassPackage(dir, "", monitor);
|
classPackage = new ClassPackage(dir, "", monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getClasses(Set<ClassFileInfo> set, TaskMonitor monitor) throws CancelledException {
|
void getClasses(List<ClassFileInfo> list, TaskMonitor monitor) throws CancelledException {
|
||||||
classPackage.getClasses(set, monitor);
|
classPackage.getClasses(list, monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getDirPath() {
|
String getDirPath() {
|
||||||
|
|
|
@ -62,8 +62,8 @@ class ClassJar implements ClassLocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getClasses(Set<ClassFileInfo> set, TaskMonitor monitor) {
|
public void getClasses(List<ClassFileInfo> list, TaskMonitor monitor) {
|
||||||
set.addAll(classes);
|
list.addAll(classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanJar(TaskMonitor monitor) throws CancelledException {
|
private void scanJar(TaskMonitor monitor) throws CancelledException {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.util.classfinder;
|
package ghidra.util.classfinder;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
|
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
@ -27,5 +27,5 @@ interface ClassLocation {
|
||||||
|
|
||||||
public static final String CLASS_EXT = ".class";
|
public static final String CLASS_EXT = ".class";
|
||||||
|
|
||||||
public void getClasses(Set<ClassFileInfo> set, TaskMonitor monitor) throws CancelledException;
|
public void getClasses(List<ClassFileInfo> list, TaskMonitor monitor) throws CancelledException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,15 +89,16 @@ class ClassPackage implements ClassLocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getClasses(Set<ClassFileInfo> set, TaskMonitor monitor) throws CancelledException {
|
public void getClasses(List<ClassFileInfo> list, TaskMonitor monitor)
|
||||||
|
throws CancelledException {
|
||||||
|
|
||||||
set.addAll(classes);
|
list.addAll(classes);
|
||||||
|
|
||||||
Iterator<ClassPackage> it = children.iterator();
|
Iterator<ClassPackage> it = children.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
ClassPackage subPkg = it.next();
|
ClassPackage subPkg = it.next();
|
||||||
subPkg.getClasses(set, monitor);
|
subPkg.getClasses(list, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,6 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.swing.event.ChangeListener;
|
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.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
@ -81,7 +79,7 @@ public class ClassSearcher {
|
||||||
private static List<Class<?>> FILTER_CLASSES = Arrays.asList(ExtensionPoint.class);
|
private static List<Class<?>> FILTER_CLASSES = Arrays.asList(ExtensionPoint.class);
|
||||||
private static Pattern extensionPointSuffixPattern;
|
private static Pattern extensionPointSuffixPattern;
|
||||||
private static Map<String, Set<ClassFileInfo>> extensionPointSuffixToInfoMap;
|
private static Map<String, Set<ClassFileInfo>> extensionPointSuffixToInfoMap;
|
||||||
private static BidiMap<ClassFileInfo, Class<?>> loadedCache = new DualHashBidiMap<>();
|
private static Map<ClassFileInfo, Class<?>> loadedCache = new HashMap<>();
|
||||||
private static Set<ClassFileInfo> falsePositiveCache = new HashSet<>();
|
private static Set<ClassFileInfo> falsePositiveCache = new HashSet<>();
|
||||||
private static volatile boolean hasSearched;
|
private static volatile boolean hasSearched;
|
||||||
private static volatile boolean isSearching;
|
private static volatile boolean isSearching;
|
||||||
|
@ -187,12 +185,6 @@ public class ClassSearcher {
|
||||||
Class<?> c = loadedCache.get(info);
|
Class<?> c = loadedCache.get(info);
|
||||||
if (c == null) {
|
if (c == null) {
|
||||||
c = loadExtensionPoint(info.path(), info.name());
|
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) {
|
if (c == null) {
|
||||||
falsePositiveCache.add(info);
|
falsePositiveCache.add(info);
|
||||||
continue;
|
continue;
|
||||||
|
@ -418,8 +410,8 @@ public class ClassSearcher {
|
||||||
throws CancelledException {
|
throws CancelledException {
|
||||||
log.info("Searching for classes...");
|
log.info("Searching for classes...");
|
||||||
|
|
||||||
Set<ClassDir> classDirs = new HashSet<>();
|
List<ClassDir> classDirs = new ArrayList<>();
|
||||||
Set<ClassJar> classJars = new HashSet<>();
|
List<ClassJar> classJars = new ArrayList<>();
|
||||||
|
|
||||||
for (String searchPath : gatherSearchPaths()) {
|
for (String searchPath : gatherSearchPaths()) {
|
||||||
String lcSearchPath = searchPath.toLowerCase();
|
String lcSearchPath = searchPath.toLowerCase();
|
||||||
|
@ -441,17 +433,31 @@ public class ClassSearcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ClassFileInfo> classSet = new HashSet<>();
|
List<ClassFileInfo> classList = new ArrayList<>();
|
||||||
for (ClassDir dir : classDirs) {
|
for (ClassDir dir : classDirs) {
|
||||||
monitor.checkCancelled();
|
monitor.checkCancelled();
|
||||||
dir.getClasses(classSet, monitor);
|
dir.getClasses(classList, monitor);
|
||||||
}
|
}
|
||||||
for (ClassJar jar : classJars) {
|
for (ClassJar jar : classJars) {
|
||||||
monitor.checkCancelled();
|
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<String, ClassFileInfo> 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()));
|
.collect(Collectors.groupingBy(ClassFileInfo::suffix, Collectors.toSet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,6 +165,15 @@ public class GhidraLauncher {
|
||||||
else { /* Eclipse dev mode */
|
else { /* Eclipse dev mode */
|
||||||
// Support loading pre-built, jar-based, non-repo extensions in Eclipse dev mode
|
// Support loading pre-built, jar-based, non-repo extensions in Eclipse dev mode
|
||||||
addExtensionJarPaths(classpathList, modules, layout);
|
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
|
// In development mode, jars do not live in module directories. Instead, each jar lives
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue