GT-3350: Consistently ordered classpath and ExtensionPoint priorities

This commit is contained in:
Ryan Kurtz 2019-11-22 11:35:56 -05:00
parent 7ab75e411e
commit 8f00152601
52 changed files with 341 additions and 169 deletions

View file

@ -17,7 +17,8 @@ package ghidra.app.util.disassemble;
import java.awt.Color; import java.awt.Color;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import docking.widgets.fieldpanel.field.*; import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
@ -43,7 +44,7 @@ public class ExternalDisassemblyFieldFactory extends FieldFactory {
availableDisassemblers = new ArrayList<>(); availableDisassemblers = new ArrayList<>();
// find the available external disassemblers // find the available external disassemblers
Set<ExternalDisassembler> extDisassemblers = List<ExternalDisassembler> extDisassemblers =
ClassSearcher.getInstances(ExternalDisassembler.class); ClassSearcher.getInstances(ExternalDisassembler.class);
for (ExternalDisassembler disassember : extDisassemblers) { for (ExternalDisassembler disassember : extDisassemblers) {

View file

@ -42,7 +42,7 @@ public class DumpGhidraCapabilitiesScript extends GhidraScript {
addPlugin(moduleName, pluginDescription); addPlugin(moduleName, pluginDescription);
} }
Set<Analyzer> instances = ClassSearcher.getInstances(Analyzer.class); List<Analyzer> instances = ClassSearcher.getInstances(Analyzer.class);
for (Analyzer analyzer : instances) { for (Analyzer analyzer : instances) {
Class<? extends Analyzer> clazz = analyzer.getClass(); Class<? extends Analyzer> clazz = analyzer.getClass();

View file

@ -68,7 +68,7 @@ public class SummarizeAnalyzers extends GhidraScript {
Options options = currentProgram.getOptions(Program.ANALYSIS_PROPERTIES); Options options = currentProgram.getOptions(Program.ANALYSIS_PROPERTIES);
Set<Class<? extends Analyzer>> classes = ClassSearcher.getClasses(Analyzer.class); List<Class<? extends Analyzer>> classes = ClassSearcher.getClasses(Analyzer.class);
for (Class<? extends Analyzer> element : classes) { for (Class<? extends Analyzer> element : classes) {
Analyzer analyzer; Analyzer analyzer;
try { try {

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.app.cmd.label; package ghidra.app.cmd.label;
import java.util.Set; import java.util.List;
import ghidra.app.util.demangler.*; import ghidra.app.util.demangler.*;
import ghidra.framework.cmd.BackgroundCommand; import ghidra.framework.cmd.BackgroundCommand;
@ -33,7 +33,7 @@ public class DemanglerCmd extends BackgroundCommand {
private String mangled; private String mangled;
private String result; private String result;
private DemangledObject demangledObject; private DemangledObject demangledObject;
private static Set<Demangler> demanglers; private static List<Demangler> demanglers;
private DemanglerOptions options; private DemanglerOptions options;
public DemanglerCmd(Address addr, String mangled) { public DemanglerCmd(Address addr, String mangled) {
@ -138,7 +138,7 @@ public class DemanglerCmd extends BackgroundCommand {
return demangledObject; return demangledObject;
} }
private static Set<Demangler> getDemanglers() { private static List<Demangler> getDemanglers() {
if (demanglers == null) { if (demanglers == null) {
demanglers = ClassSearcher.getInstances(Demangler.class); demanglers = ClassSearcher.getInstances(Demangler.class);
} }

View file

@ -158,7 +158,7 @@ public class AutoAnalysisManager implements DomainObjectListener, DomainObjectCl
taskArray = new AnalysisTaskList[] { byteTasks, instructionTasks, functionTasks, taskArray = new AnalysisTaskList[] { byteTasks, instructionTasks, functionTasks,
functionModifierChangedTasks, functionSignatureChangedTasks, dataTasks }; functionModifierChangedTasks, functionSignatureChangedTasks, dataTasks };
Set<Analyzer> analyzers = ClassSearcher.getInstances(Analyzer.class); List<Analyzer> analyzers = ClassSearcher.getInstances(Analyzer.class);
for (Analyzer analyzer : analyzers) { for (Analyzer analyzer : analyzers) {
if (!analyzer.canAnalyze(program)) { if (!analyzer.canAnalyze(program)) {
continue; continue;

View file

@ -19,15 +19,13 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.util.MemoryByteIterator; import ghidra.program.model.util.MemoryByteIterator;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
/** /**
* This class is used for the computation of various basic checksums. * This class is used for the computation of various basic checksums.
*/ */
@ExtensionPoint.Exclude(reason = "Subclasses will get picked up by extension points.") public abstract class BasicChecksumAlgorithm extends ChecksumAlgorithm {
public class BasicChecksumAlgorithm extends ChecksumAlgorithm {
/** /**
* The byte sizes that are supported by the basic checksum algorithm. * The byte sizes that are supported by the basic checksum algorithm.

View file

@ -17,7 +17,8 @@ package ghidra.app.plugin.core.checksums;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.Color; import java.awt.Color;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import javax.swing.*; import javax.swing.*;
@ -63,7 +64,7 @@ public class ComputeChecksumsProvider extends ComponentProviderAdapter {
super(plugin.getTool(), "Checksum Generator", plugin.getName(), ProgramContextAction.class); super(plugin.getTool(), "Checksum Generator", plugin.getName(), ProgramContextAction.class);
setHelpLocation(new HelpLocation("ComputeChecksumsPlugin", "Generate_Checksum_Help")); setHelpLocation(new HelpLocation("ComputeChecksumsPlugin", "Generate_Checksum_Help"));
Set<ChecksumAlgorithm> algorithms = ClassSearcher.getInstances(ChecksumAlgorithm.class); List<ChecksumAlgorithm> algorithms = ClassSearcher.getInstances(ChecksumAlgorithm.class);
checksums.addAll(algorithms); checksums.addAll(algorithms);
this.plugin = plugin; this.plugin = plugin;

View file

@ -22,7 +22,6 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryAccessException; import ghidra.program.model.mem.MemoryAccessException;
import ghidra.program.model.util.MemoryByteIterator; import ghidra.program.model.util.MemoryByteIterator;
import ghidra.util.classfinder.ExtensionPoint;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ -30,8 +29,7 @@ import ghidra.util.task.TaskMonitor;
* This class is used for the computation of various digest checksums that are provided * This class is used for the computation of various digest checksums that are provided
* by java. These checksums do not have options associated with them. * by java. These checksums do not have options associated with them.
*/ */
@ExtensionPoint.Exclude(reason = "Subclasses will get picked up by extension points.") public abstract class DigestChecksumAlgorithm extends ChecksumAlgorithm {
public class DigestChecksumAlgorithm extends ChecksumAlgorithm {
MessageDigest digester; MessageDigest digester;

View file

@ -112,7 +112,7 @@ public class ExportToHeaderAction extends DockingAction {
*/ */
private void exportToC(GTree gTree, DataTypeManager programDataTypeMgr) { private void exportToC(GTree gTree, DataTypeManager programDataTypeMgr) {
Set<Class<? extends AnnotationHandler>> classes = List<Class<? extends AnnotationHandler>> classes =
ClassSearcher.getClasses(AnnotationHandler.class); ClassSearcher.getClasses(AnnotationHandler.class);
List<AnnotationHandler> list = new ArrayList<>(); List<AnnotationHandler> list = new ArrayList<>();

View file

@ -154,7 +154,7 @@ public class FunctionComparisonPanel extends JPanel implements ChangeListener {
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private Set<CodeComparisonPanel<? extends FieldPanelCoordinator>> createAllPossibleCodeComparisonPanels() { private Set<CodeComparisonPanel<? extends FieldPanelCoordinator>> createAllPossibleCodeComparisonPanels() {
Set<CodeComparisonPanel<? extends FieldPanelCoordinator>> instances = new HashSet<>(); Set<CodeComparisonPanel<? extends FieldPanelCoordinator>> instances = new HashSet<>();
Set<Class<? extends CodeComparisonPanel>> classes = List<Class<? extends CodeComparisonPanel>> classes =
ClassSearcher.getClasses(CodeComparisonPanel.class); ClassSearcher.getClasses(CodeComparisonPanel.class);
for (Class<? extends CodeComparisonPanel> panelClass : classes) { for (Class<? extends CodeComparisonPanel> panelClass : classes) {
try { try {

View file

@ -284,7 +284,7 @@ public final class ReferenceUtils {
Accumulator<LocationReference> accumulator, Program program, DataType dataType, Accumulator<LocationReference> accumulator, Program program, DataType dataType,
String fieldName, TaskMonitor monitor) throws CancelledException { String fieldName, TaskMonitor monitor) throws CancelledException {
Set<DataTypeReferenceFinder> finders = List<DataTypeReferenceFinder> finders =
ClassSearcher.getInstances(DataTypeReferenceFinder.class); ClassSearcher.getInstances(DataTypeReferenceFinder.class);
Consumer<DataTypeReference> callback = ref -> { Consumer<DataTypeReference> callback = ref -> {

View file

@ -54,7 +54,7 @@ import resources.ResourceManager;
public class OverviewColorPlugin extends ProgramPlugin { public class OverviewColorPlugin extends ProgramPlugin {
public static final String HELP_TOPIC = "OverviewPlugin"; public static final String HELP_TOPIC = "OverviewPlugin";
private static final String ACTIVE_SERVICES = "ActiveServices"; private static final String ACTIVE_SERVICES = "ActiveServices";
private Set<OverviewColorService> allServices; private List<OverviewColorService> allServices;
private Map<OverviewColorService, OverviewColorComponent> activeServices = private Map<OverviewColorService, OverviewColorComponent> activeServices =
new LinkedHashMap<>(); // maintain the left to right order of the active overview bars. new LinkedHashMap<>(); // maintain the left to right order of the active overview bars.
private CodeViewerService codeViewerService; private CodeViewerService codeViewerService;

View file

@ -15,18 +15,6 @@
*/ */
package ghidra.app.plugin.core.validator; package ghidra.app.plugin.core.validator;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.analysis.validator.PostAnalysisValidator;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.*;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.*; import java.util.*;
@ -35,6 +23,17 @@ import docking.action.DockingAction;
import docking.action.MenuData; import docking.action.MenuData;
import docking.tool.ToolConstants; import docking.tool.ToolConstants;
import docking.widgets.conditiontestpanel.ConditionTester; import docking.widgets.conditiontestpanel.ConditionTester;
import ghidra.app.CorePluginPackage;
import ghidra.app.context.ProgramActionContext;
import ghidra.app.context.ProgramContextAction;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.analysis.validator.PostAnalysisValidator;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
/** /**
* Display a pop-up dialog to run PostAnalysisValidator tests on the Program * Display a pop-up dialog to run PostAnalysisValidator tests on the Program
@ -117,7 +116,7 @@ public class ValidateProgramPlugin extends Plugin {
private List<ConditionTester> getConditionTests(Program program) { private List<ConditionTester> getConditionTests(Program program) {
List<ConditionTester> list = new ArrayList<ConditionTester>(); List<ConditionTester> list = new ArrayList<ConditionTester>();
Set<Class<? extends PostAnalysisValidator>> validatorClasses = List<Class<? extends PostAnalysisValidator>> validatorClasses =
ClassSearcher.getClasses(PostAnalysisValidator.class); ClassSearcher.getClasses(PostAnalysisValidator.class);
for (Class<? extends PostAnalysisValidator> validatorClass : validatorClasses) { for (Class<? extends PostAnalysisValidator> validatorClass : validatorClasses) {
try { try {

View file

@ -16,7 +16,6 @@
package ghidra.app.util.demangler; package ghidra.app.util.demangler;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -39,7 +38,7 @@ public class DemanglerUtil {
* @return the demangled object or null * @return the demangled object or null
*/ */
public static DemangledObject demangle(String mangled) { public static DemangledObject demangle(String mangled) {
Set<Demangler> demanglers = getDemanglers(); List<Demangler> demanglers = getDemanglers();
for (Demangler demangler : demanglers) { for (Demangler demangler : demanglers) {
try { try {
// not sure if we should be doing all symbols, but this is what it used to do // not sure if we should be doing all symbols, but this is what it used to do
@ -65,7 +64,7 @@ public class DemanglerUtil {
* @return the demangled object or null * @return the demangled object or null
*/ */
public static DemangledObject demangle(Program program, String mangled) { public static DemangledObject demangle(Program program, String mangled) {
Set<Demangler> demanglers = getDemanglers(); List<Demangler> demanglers = getDemanglers();
for (Demangler demangler : demanglers) { for (Demangler demangler : demanglers) {
try { try {
if (!demangler.canDemangle(program)) { if (!demangler.canDemangle(program)) {
@ -91,7 +90,7 @@ public class DemanglerUtil {
* *
* @return a list of all demanglers * @return a list of all demanglers
*/ */
private static Set<Demangler> getDemanglers() { private static List<Demangler> getDemanglers() {
return ClassSearcher.getInstances(Demangler.class); return ClassSearcher.getInstances(Demangler.class);
} }

View file

@ -52,7 +52,7 @@ public class Annotation {
Map<String, AnnotatedStringHandler> map = new HashMap<>(); Map<String, AnnotatedStringHandler> map = new HashMap<>();
// find all instances of AnnotatedString // find all instances of AnnotatedString
Set<AnnotatedStringHandler> instances = List<AnnotatedStringHandler> instances =
ClassSearcher.getInstances(AnnotatedStringHandler.class); ClassSearcher.getInstances(AnnotatedStringHandler.class);
for (AnnotatedStringHandler instance : instances) { for (AnnotatedStringHandler instance : instances) {

View file

@ -15,7 +15,8 @@
*/ */
package ghidra.app.util.viewer.format; package ghidra.app.util.viewer.format;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import org.jdom.Element; import org.jdom.Element;
@ -139,7 +140,7 @@ public class FormatManager implements OptionsChangeListener {
private void getFactorys() { private void getFactorys() {
ClassFilter filter = new ClassExclusionFilter(DummyFieldFactory.class, ClassFilter filter = new ClassExclusionFilter(DummyFieldFactory.class,
SpacerFieldFactory.class, SubDataFieldFactory.class); SpacerFieldFactory.class, SubDataFieldFactory.class);
Set<FieldFactory> instances = ClassSearcher.getInstances(FieldFactory.class, filter); List<FieldFactory> instances = ClassSearcher.getInstances(FieldFactory.class, filter);
List<FieldFactory> list = new ArrayList<>(); List<FieldFactory> list = new ArrayList<>();
for (FieldFactory fieldFactory : instances) { for (FieldFactory fieldFactory : instances) {
if (fieldFactory instanceof SpacerFieldFactory) { if (fieldFactory instanceof SpacerFieldFactory) {

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +15,10 @@
*/ */
package ghidra.app.util.viewer.util; package ghidra.app.util.viewer.util;
import java.awt.event.MouseEvent;
import java.util.*;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
import ghidra.app.services.*; import ghidra.app.services.*;
import ghidra.app.util.viewer.field.*; import ghidra.app.util.viewer.field.*;
@ -23,11 +26,6 @@ import ghidra.framework.plugintool.ServiceProvider;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import java.awt.event.MouseEvent;
import java.util.*;
import docking.widgets.fieldpanel.support.FieldLocation;
/** /**
* Helper class to navigate to an address when user double clicks in a * Helper class to navigate to an address when user double clicks in a
* Field. This class will find {@link FieldMouseHandlerExtension}s by using the {@link ClassSearcher}. * Field. This class will find {@link FieldMouseHandlerExtension}s by using the {@link ClassSearcher}.
@ -84,7 +82,7 @@ public class FieldNavigator implements ButtonPressedListener, FieldMouseHandlerS
new HashMap<Class<?>, List<FieldMouseHandler>>(); new HashMap<Class<?>, List<FieldMouseHandler>>();
// find all instances of AnnotatedString // find all instances of AnnotatedString
Set<FieldMouseHandlerExtension> instances = List<FieldMouseHandlerExtension> instances =
ClassSearcher.getInstances(FieldMouseHandlerExtension.class); ClassSearcher.getInstances(FieldMouseHandlerExtension.class);
for (FieldMouseHandlerExtension fieldMouseHandler : instances) { for (FieldMouseHandlerExtension fieldMouseHandler : instances) {
addHandler(map, fieldMouseHandler); addHandler(map, fieldMouseHandler);

View file

@ -16,7 +16,7 @@
package ghidra.framework; package ghidra.framework;
import java.io.File; import java.io.File;
import java.util.Set; import java.util.List;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.GhidraClassLoader; import ghidra.GhidraClassLoader;
@ -111,7 +111,7 @@ public class HeadlessGhidraApplicationConfiguration extends ApplicationConfigura
} }
private void performModuleInitialization() { private void performModuleInitialization() {
Set<ModuleInitializer> instances = ClassSearcher.getInstances(ModuleInitializer.class); List<ModuleInitializer> instances = ClassSearcher.getInstances(ModuleInitializer.class);
for (ModuleInitializer initializer : instances) { for (ModuleInitializer initializer : instances) {
monitor.setMessage("Initializing " + initializer.getName() + "..."); monitor.setMessage("Initializing " + initializer.getName() + "...");
initializer.run(); initializer.run();

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,12 +15,13 @@
*/ */
package ghidra.framework.analysis; package ghidra.framework.analysis;
import java.util.ArrayList;
import java.util.List;
import ghidra.app.services.Analyzer; import ghidra.app.services.Analyzer;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import java.util.*;
public class AnalysisRecipeBuilder { public class AnalysisRecipeBuilder {
private static List<Class<? extends Analyzer>> classes; private static List<Class<? extends Analyzer>> classes;
@ -39,7 +39,7 @@ public class AnalysisRecipeBuilder {
private static AnalysisRecipe buildDefaultRecipe(Program program) { private static AnalysisRecipe buildDefaultRecipe(Program program) {
List<Analyzer> analyzerList = new ArrayList<Analyzer>(); List<Analyzer> analyzerList = new ArrayList<Analyzer>();
Set<Analyzer> anayzers = ClassSearcher.getInstances(Analyzer.class); List<Analyzer> anayzers = ClassSearcher.getInstances(Analyzer.class);
for (Analyzer analyzer : anayzers) { for (Analyzer analyzer : anayzers) {
if (analyzer.canAnalyze(program)) { if (analyzer.canAnalyze(program)) {
analyzerList.add(analyzer); analyzerList.add(analyzer);

View file

@ -27,11 +27,11 @@ import ghidra.program.model.listing.Program;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.classfinder.ExtensionPoint; import ghidra.util.classfinder.ExtensionPointProperties;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
@ExtensionPoint.Exclude(reason = "requires explicit instantiation to wrap a script") @ExtensionPointProperties(exclude = true) // exclude class from extension point discovery because it has to be directly instantiated in order to wrap the supplied script
public class GhidraScriptAnalyzerAdapter extends AbstractAnalyzer { public class GhidraScriptAnalyzerAdapter extends AbstractAnalyzer {
private ResourceFile scriptFile; private ResourceFile scriptFile;

View file

@ -18,7 +18,6 @@ package ghidra.framework.plugintool.dialog;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.List; import java.util.List;
import java.util.Set;
import org.junit.*; import org.junit.*;
@ -88,7 +87,7 @@ public class ManageFrontEndToolTest extends AbstractGhidraHeadedIntegrationTest
count++; count++;
} }
Set<Class<? extends FrontEndable>> classes = ClassSearcher.getClasses(FrontEndable.class); List<Class<? extends FrontEndable>> classes = ClassSearcher.getClasses(FrontEndable.class);
assertEquals(count, classes.size()); assertEquals(count, classes.size());
} }

View file

@ -17,6 +17,7 @@ package ghidra.app.plugin.core.byteviewer;
import java.awt.event.*; import java.awt.event.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List;
import java.util.Set; import java.util.Set;
import javax.swing.*; import javax.swing.*;
@ -696,7 +697,7 @@ public class ProgramByteViewerComponentProvider extends ByteViewerComponentProvi
@Override @Override
protected Set<DataFormatModel> getDataFormatModels() { protected Set<DataFormatModel> getDataFormatModels() {
Set<DataFormatModel> dataFormatModels = super.getDataFormatModels(); Set<DataFormatModel> dataFormatModels = super.getDataFormatModels();
Set<ProgramDataFormatModel> instances = List<ProgramDataFormatModel> instances =
ClassSearcher.getInstances(ProgramDataFormatModel.class); ClassSearcher.getInstances(ProgramDataFormatModel.class);
dataFormatModels.addAll(instances); dataFormatModels.addAll(instances);
return dataFormatModels; return dataFormatModels;

View file

@ -15,13 +15,13 @@
*/ */
package ghidra.file.analyzers; package ghidra.file.analyzers;
import ghidra.util.classfinder.ClassSearcher; import java.util.List;
import java.util.Set; import ghidra.util.classfinder.ClassSearcher;
public final class FileFormatAnalyzerFactory { public final class FileFormatAnalyzerFactory {
public final static Set<FileFormatAnalyzer> getAnalyzers() { public final static List<FileFormatAnalyzer> getAnalyzers() {
return ClassSearcher.getInstances(FileFormatAnalyzer.class); return ClassSearcher.getInstances(FileFormatAnalyzer.class);
} }
} }

View file

@ -15,15 +15,15 @@
*/ */
package ghidra.file.crypto; package ghidra.file.crypto;
import java.io.IOException;
import java.util.List;
import ghidra.app.util.bin.ByteProvider; import ghidra.app.util.bin.ByteProvider;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.CryptoException; import ghidra.util.exception.CryptoException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import java.io.IOException;
import java.util.Set;
/** /**
* Not used by any known code, not tested. * Not used by any known code, not tested.
*/ */
@ -34,7 +34,7 @@ public final class DecryptorFactory {
ByteProvider provider, TaskMonitor monitor) throws IOException, CryptoException, ByteProvider provider, TaskMonitor monitor) throws IOException, CryptoException,
CancelledException { CancelledException {
Set<Decryptor> instances = ClassSearcher.getInstances(Decryptor.class); List<Decryptor> instances = ClassSearcher.getInstances(Decryptor.class);
for (Decryptor decryptor : instances) { for (Decryptor decryptor : instances) {
if (monitor.isCancelled()) { if (monitor.isCancelled()) {
throw new CancelledException(); throw new CancelledException();

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.functiongraph; package ghidra.app.plugin.core.functiongraph;
import java.util.Set; import java.util.List;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
@ -27,8 +27,8 @@ import ghidra.util.classfinder.ClassSearcher;
public class DiscoverableFGLayoutFinder implements FGLayoutFinder { public class DiscoverableFGLayoutFinder implements FGLayoutFinder {
@Override @Override
public Set<FGLayoutProvider> findLayouts() { public List<FGLayoutProvider> findLayouts() {
Set<FGLayoutProvider> instances = ClassSearcher.getInstances(FGLayoutProvider.class); List<FGLayoutProvider> instances = ClassSearcher.getInstances(FGLayoutProvider.class);
return instances; return instances;
} }

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.app.plugin.core.functiongraph; package ghidra.app.plugin.core.functiongraph;
import java.util.Set; import java.util.List;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayout;
import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider; import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
@ -24,5 +24,5 @@ import ghidra.app.plugin.core.functiongraph.graph.layout.FGLayoutProvider;
* An interface that provides {@link FGLayout}s * An interface that provides {@link FGLayout}s
*/ */
public interface FGLayoutFinder { public interface FGLayoutFinder {
public Set<FGLayoutProvider> findLayouts(); public List<FGLayoutProvider> findLayouts();
} }

View file

@ -135,7 +135,7 @@ public class FunctionGraphPlugin extends ProgramPlugin implements OptionsChangeL
private List<FGLayoutProvider> loadLayoutProviders() { private List<FGLayoutProvider> loadLayoutProviders() {
FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder(); FGLayoutFinder layoutFinder = new DiscoverableFGLayoutFinder();
Set<FGLayoutProvider> instances = layoutFinder.findLayouts(); List<FGLayoutProvider> instances = layoutFinder.findLayouts();
if (instances.isEmpty()) { if (instances.isEmpty()) {
throw new AssertException("Could not find any layout providers. You project may not " + throw new AssertException("Could not find any layout providers. You project may not " +
"be configured properly."); "be configured properly.");

View file

@ -59,7 +59,7 @@ public class FidService {
this.digestFactory = new FNV1a64MessageDigestFactory(); this.digestFactory = new FNV1a64MessageDigestFactory();
this.skippers = new HashMap<>(); this.skippers = new HashMap<>();
Set<Class<? extends InstructionSkipper>> classes = List<Class<? extends InstructionSkipper>> classes =
ClassSearcher.getClasses(InstructionSkipper.class); ClassSearcher.getClasses(InstructionSkipper.class);
for (Class<? extends InstructionSkipper> clazz : classes) { for (Class<? extends InstructionSkipper> clazz : classes) {
try { try {

View file

@ -15,6 +15,9 @@
*/ */
package ghidra.feature.vt; package ghidra.feature.vt;
import java.io.IOException;
import java.util.*;
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.feature.vt.api.db.VTSessionDB; import ghidra.feature.vt.api.db.VTSessionDB;
import ghidra.feature.vt.api.main.*; import ghidra.feature.vt.api.main.*;
@ -26,9 +29,6 @@ import ghidra.util.classfinder.ClassSearcher;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException; import ghidra.util.exception.VersionException;
import java.io.IOException;
import java.util.*;
public abstract class GhidraVersionTrackingScript extends GhidraScript { public abstract class GhidraVersionTrackingScript extends GhidraScript {
protected VTSession vtSession; protected VTSession vtSession;
protected Program sourceProgram; protected Program sourceProgram;
@ -162,7 +162,7 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
public List<String> getProgramCorrelators() { public List<String> getProgramCorrelators() {
List<String> correlators = new ArrayList<String>(); List<String> correlators = new ArrayList<String>();
Set<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory(); List<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory();
for (VTProgramCorrelatorFactory vtProgramCorrelatorFactory : generateList) { for (VTProgramCorrelatorFactory vtProgramCorrelatorFactory : generateList) {
correlators.add(vtProgramCorrelatorFactory.getName()); correlators.add(vtProgramCorrelatorFactory.getName());
} }
@ -221,7 +221,7 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
//================================================================================================== //==================================================================================================
private VTProgramCorrelatorFactory getCorrelatorFactory(String name) { private VTProgramCorrelatorFactory getCorrelatorFactory(String name) {
Set<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory(); List<VTProgramCorrelatorFactory> generateList = getVTProgramCorrelatorFactory();
for (VTProgramCorrelatorFactory vtProgramCorrelatorFactory : generateList) { for (VTProgramCorrelatorFactory vtProgramCorrelatorFactory : generateList) {
if (vtProgramCorrelatorFactory.getName().equals(name)) { if (vtProgramCorrelatorFactory.getName().equals(name)) {
return vtProgramCorrelatorFactory; return vtProgramCorrelatorFactory;
@ -230,7 +230,7 @@ public abstract class GhidraVersionTrackingScript extends GhidraScript {
return null; return null;
} }
private static Set<VTProgramCorrelatorFactory> getVTProgramCorrelatorFactory() { private static List<VTProgramCorrelatorFactory> getVTProgramCorrelatorFactory() {
return ClassSearcher.getInstances(VTProgramCorrelatorFactory.class); return ClassSearcher.getInstances(VTProgramCorrelatorFactory.class);
} }
} }

View file

@ -40,7 +40,7 @@ public abstract class Stringable implements ExtensionPoint, DisplayStringProvide
private static void initializeNameMap() { private static void initializeNameMap() {
shortNameToClassnameMap = new HashMap<String, Class<? extends Stringable>>(); shortNameToClassnameMap = new HashMap<String, Class<? extends Stringable>>();
Set<Class<? extends Stringable>> classes = ClassSearcher.getClasses(Stringable.class); List<Class<? extends Stringable>> classes = ClassSearcher.getClasses(Stringable.class);
for (Class<? extends Stringable> clazz : classes) { for (Class<? extends Stringable> clazz : classes) {
String name = getShortNameFieldValue(clazz); String name = getShortNameFieldValue(clazz);
shortNameToClassnameMap.put(name, clazz); shortNameToClassnameMap.put(name, clazz);

View file

@ -62,7 +62,7 @@ public class AddressCorrelatorManager {
private List<AddressCorrelator> initializeAddressCorrelators() { private List<AddressCorrelator> initializeAddressCorrelators() {
Set<DiscoverableAddressCorrelator> instances = List<DiscoverableAddressCorrelator> instances =
ClassSearcher.getInstances(DiscoverableAddressCorrelator.class); ClassSearcher.getInstances(DiscoverableAddressCorrelator.class);
List<AddressCorrelator> addressCorrelatorList = new ArrayList<AddressCorrelator>(instances); List<AddressCorrelator> addressCorrelatorList = new ArrayList<AddressCorrelator>(instances);

View file

@ -17,7 +17,8 @@ package ghidra.feature.vt.gui.provider.matchtable;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.awt.event.*; import java.awt.event.*;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import javax.swing.*; import javax.swing.*;
@ -98,7 +99,7 @@ public class AlgorithmFilter extends CheckBoxBasedAncillaryFilter<VTMatch> {
list.add(ManualMatchProgramCorrelator.NAME); list.add(ManualMatchProgramCorrelator.NAME);
list.add(ImpliedMatchProgramCorrelator.NAME); list.add(ImpliedMatchProgramCorrelator.NAME);
Set<VTAbstractProgramCorrelatorFactory> instances = List<VTAbstractProgramCorrelatorFactory> instances =
ClassSearcher.getInstances(VTAbstractProgramCorrelatorFactory.class); ClassSearcher.getInstances(VTAbstractProgramCorrelatorFactory.class);
for (VTAbstractProgramCorrelatorFactory factory : instances) { for (VTAbstractProgramCorrelatorFactory factory : instances) {
list.add(factory.getName()); list.add(factory.getName());

View file

@ -15,13 +15,6 @@
*/ */
package ghidra.feature.vt.gui.wizard; package ghidra.feature.vt.gui.wizard;
import ghidra.feature.vt.api.main.VTSession;
import ghidra.feature.vt.gui.validator.VTPreconditionValidator;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
@ -33,6 +26,12 @@ import javax.swing.*;
import docking.widgets.conditiontestpanel.*; import docking.widgets.conditiontestpanel.*;
import docking.wizard.*; import docking.wizard.*;
import ghidra.feature.vt.api.main.VTSession;
import ghidra.feature.vt.gui.validator.VTPreconditionValidator;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher;
public class PreconditionsPanel extends AbstractMageJPanel<VTWizardStateKey> implements Scrollable { public class PreconditionsPanel extends AbstractMageJPanel<VTWizardStateKey> implements Scrollable {
private static final Dimension DEFAULT_SIZE = new Dimension(650, 480); private static final Dimension DEFAULT_SIZE = new Dimension(650, 480);
@ -148,7 +147,7 @@ public class PreconditionsPanel extends AbstractMageJPanel<VTWizardStateKey> imp
Program destinationProgram, VTSession existingResults) throws SecurityException { Program destinationProgram, VTSession existingResults) throws SecurityException {
List<ConditionTester> list = new ArrayList<ConditionTester>(); List<ConditionTester> list = new ArrayList<ConditionTester>();
Set<Class<? extends VTPreconditionValidator>> vtValidatorClasses = List<Class<? extends VTPreconditionValidator>> vtValidatorClasses =
ClassSearcher.getClasses(VTPreconditionValidator.class); ClassSearcher.getClasses(VTPreconditionValidator.class);
for (Class<? extends VTPreconditionValidator> validatorClass : vtValidatorClasses) { for (Class<? extends VTPreconditionValidator> validatorClass : vtValidatorClasses) {
try { try {

View file

@ -60,13 +60,12 @@ public class VTProgramTableCorrelatorModel extends AbstractGTableModel<VTProgram
} }
private static List<VTProgramCorrelatorFactory> generateList() { private static List<VTProgramCorrelatorFactory> generateList() {
Set<VTAbstractProgramCorrelatorFactory> instances = List<VTAbstractProgramCorrelatorFactory> instances =
ClassSearcher.getInstances(VTAbstractProgramCorrelatorFactory.class); ClassSearcher.getInstances(VTAbstractProgramCorrelatorFactory.class);
List<VTProgramCorrelatorFactory> list = List<VTProgramCorrelatorFactory> list = new ArrayList<>(instances);
new ArrayList<>(instances);
Collections.sort(list, comparator); Collections.sort(instances, comparator);
return list; return list;
} }

View file

@ -208,7 +208,7 @@ public class DiscoverableTableUtils {
Set<TableRowMapper<ROW_TYPE, EXPECTED_TYPE, DATA_SOURCE>> set = new HashSet<>(); Set<TableRowMapper<ROW_TYPE, EXPECTED_TYPE, DATA_SOURCE>> set = new HashSet<>();
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
Set<TableRowMapper> instances = ClassSearcher.getInstances(TableRowMapper.class); List<TableRowMapper> instances = ClassSearcher.getInstances(TableRowMapper.class);
for (TableRowMapper<ROW_TYPE, EXPECTED_TYPE, DATA_SOURCE> mapper : instances) { for (TableRowMapper<ROW_TYPE, EXPECTED_TYPE, DATA_SOURCE> mapper : instances) {
if (mapper.getSourceType() == fromType && mapper.getDestinationType() == toType) { if (mapper.getSourceType() == fromType && mapper.getDestinationType() == toType) {
set.add(mapper); set.add(mapper);
@ -293,7 +293,7 @@ public class DiscoverableTableUtils {
List<ColumnConstraint<?>> mappedConstraints = new ArrayList<>(); List<ColumnConstraint<?>> mappedConstraints = new ArrayList<>();
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
Set<ColumnTypeMapper> mappers = ClassSearcher.getInstances(ColumnTypeMapper.class); List<ColumnTypeMapper> mappers = ClassSearcher.getInstances(ColumnTypeMapper.class);
for (ColumnTypeMapper<?, ?> mapper : mappers) { for (ColumnTypeMapper<?, ?> mapper : mappers) {
mappedConstraints.addAll(generateMappedConstraints(mapper, foundConstraints)); mappedConstraints.addAll(generateMappedConstraints(mapper, foundConstraints));
@ -371,7 +371,7 @@ public class DiscoverableTableUtils {
private static List<ColumnConstraint<?>> findColumnConstraints() { private static List<ColumnConstraint<?>> findColumnConstraints() {
List<ColumnConstraint<?>> constraints = new ArrayList<>(); List<ColumnConstraint<?>> constraints = new ArrayList<>();
Set<ColumnConstraintProvider> constraintProviders = List<ColumnConstraintProvider> constraintProviders =
ClassSearcher.getInstances(ColumnConstraintProvider.class); ClassSearcher.getInstances(ColumnConstraintProvider.class);
for (ColumnConstraintProvider provider : constraintProviders) { for (ColumnConstraintProvider provider : constraintProviders) {

View file

@ -72,21 +72,43 @@ public class ClassFinder {
} }
} }
Set<Class<?>> getClasses(TaskMonitor monitor) throws CancelledException { List<Class<?>> getClasses(TaskMonitor monitor) throws CancelledException {
Set<Class<?>> classes = new HashSet<>(); Set<Class<?>> classSet = new HashSet<>();
for (ClassDir dir : classDirs) { for (ClassDir dir : classDirs) {
monitor.checkCanceled(); monitor.checkCanceled();
dir.getClasses(classes, monitor); dir.getClasses(classSet, monitor);
} }
for (ClassJar jar : classJars) { for (ClassJar jar : classJars) {
monitor.checkCanceled(); monitor.checkCanceled();
jar.getClasses(classes, monitor); jar.getClasses(classSet, monitor);
} }
return classes; List<Class<?>> classList = new ArrayList<>(classSet);
Collections.sort(classList, (c1, c2) -> {
// Sort classes primarily by priority and secondarily by name
int p1 = ExtensionPointProperties.Util.getPriority(c1);
int p2 = ExtensionPointProperties.Util.getPriority(c2);
if (p1 > p2) {
return -1;
}
if (p1 < p2) {
return 1;
}
String n1 = c1.getName();
String n2 = c2.getName();
if (n1.equals(n2)) {
// Same priority and same package/class name....just arbitrarily choose one
return Integer.compare(c1.hashCode(), c2.hashCode());
}
return n1.compareTo(n2);
});
return classList;
} }
/*package*/ static Class<?> loadExtensionPoint(String path, String fullName) { /*package*/ static Class<?> loadExtensionPoint(String path, String fullName) {
@ -178,7 +200,7 @@ public class ClassFinder {
if (!Modifier.isPublic(c.getModifiers())) { if (!Modifier.isPublic(c.getModifiers())) {
return false; return false;
} }
if (ExtensionPoint.Util.isExcluded(c)) { if (ExtensionPointProperties.Util.isExcluded(c)) {
return false; return false;
} }

View file

@ -66,7 +66,7 @@ public class ClassSearcher {
static final Logger log = LogManager.getLogger(ClassSearcher.class); static final Logger log = LogManager.getLogger(ClassSearcher.class);
private static ClassFinder searcher; private static ClassFinder searcher;
private static Set<Class<?>> extensionPoints; private static List<Class<?>> extensionPoints;
private static WeakSet<ChangeListener> listenerList = private static WeakSet<ChangeListener> listenerList =
WeakDataStructureFactory.createCopyOnReadWeakSet(); WeakDataStructureFactory.createCopyOnReadWeakSet();
@ -82,53 +82,67 @@ public class ClassSearcher {
} }
/** /**
* Get classes that implement or derive from the given class * Get {@link ExtensionPointProperties#priority() priority-sorted} classes that implement or
* derive from the given class
* *
* @param c class filter class * @param c the filter class
* @return set of classes that implement or extend T * @return set of classes that implement or extend T
*/ */
public static <T> Set<Class<? extends T>> getClasses(Class<T> c) { public static <T> List<Class<? extends T>> getClasses(Class<T> c) {
return getClasses(c, null); return getClasses(c, null);
} }
/** /**
* Get classes that implement or derive from the given class * Get {@link ExtensionPointProperties#priority() priority-sorted} classes that
* implement or derive from the given class
* *
* @param c class filter class * @param c the filter class
* @param classFilter A Predicate that tests class objects (that are already of type T) * @param classFilter A Predicate that tests class objects (that are already of type T)
* for further filtering, <code>null</code> is equivalent to "return true" * for further filtering, <code>null</code> is equivalent to "return true"
* @return set of classes that implement or extend T and pass the filtering test performed * @return {@link ExtensionPointProperties#priority() priority-sorted} list of
* by the predicate. * classes that implement or extend T and pass the filtering test performed by the
* predicate
*/ */
@SuppressWarnings("unchecked") // we checked the type of each use so we know the casts are safe @SuppressWarnings("unchecked") // we checked the type of each use so we know the casts are safe
public static <T> Set<Class<? extends T>> getClasses(Class<T> c, public static <T> List<Class<? extends T>> getClasses(Class<T> c,
Predicate<Class<? extends T>> classFilter) { Predicate<Class<? extends T>> classFilter) {
if (isSearching) { if (isSearching) {
throw new IllegalStateException( throw new IllegalStateException(
"Cannot call the getClasses() while the ClassSearcher is searching!"); "Cannot call the getClasses() while the ClassSearcher is searching!");
} }
Set<Class<? extends T>> set = new HashSet<>(); List<Class<? extends T>> list = new ArrayList<>();
if (extensionPoints == null) { if (extensionPoints == null) {
return set; return list;
} }
for (Class<?> extensionPoint : extensionPoints) { for (Class<?> extensionPoint : extensionPoints) {
if (c.isAssignableFrom(extensionPoint) && if (c.isAssignableFrom(extensionPoint) &&
(classFilter == null || classFilter.test((Class<T>) extensionPoint))) { (classFilter == null || classFilter.test((Class<T>) extensionPoint))) {
set.add((Class<? extends T>) extensionPoint); list.add((Class<? extends T>) extensionPoint);
} }
} }
return set; return list;
} }
public static <T> Set<T> getInstances(Class<T> c) { public static <T> List<T> getInstances(Class<T> c) {
return getInstances(c, DO_NOTHING_FILTER); return getInstances(c, DO_NOTHING_FILTER);
} }
public static <T> Set<T> getInstances(Class<T> c, ClassFilter filter) { /**
Set<Class<? extends T>> classes = getClasses(c); * Get {@link ExtensionPointProperties#priority() priority-sorted} classes
Set<T> instances = new HashSet<>(); * instances that implement or derive from the given class
*
* @param c the filter class
* @param filter A Predicate that tests class objects (that are already of type T)
* for further filtering, <code>null</code> is equivalent to "return true"
* @return {@link ExtensionPointProperties#priority() priority-sorted} list of
* classes instances that implement or extend T and pass the filtering test performed by
* the predicate
*/
public static <T> List<T> getInstances(Class<T> c, ClassFilter filter) {
List<Class<? extends T>> classes = getClasses(c);
List<T> instances = new ArrayList<>();
for (Class<? extends T> clazz : classes) { for (Class<? extends T> clazz : classes) {
if (!filter.accepts(clazz)) { if (!filter.accepts(clazz)) {
@ -298,7 +312,7 @@ public class ClassSearcher {
ResourceFile extensionClassesFile = new ResourceFile(appRoot, "EXTENSION_POINT_CLASSES"); ResourceFile extensionClassesFile = new ResourceFile(appRoot, "EXTENSION_POINT_CLASSES");
try { try {
List<String> classNames = FileUtilities.getLines(extensionClassesFile); List<String> classNames = FileUtilities.getLines(extensionClassesFile);
Set<Class<?>> extensionClasses = new HashSet<>(); List<Class<?>> extensionClasses = new ArrayList<>();
for (String className : classNames) { for (String className : classNames) {
try { try {
Class<?> clazz = Class.forName(className); Class<?> clazz = Class.forName(className);
@ -308,7 +322,7 @@ public class ClassSearcher {
Msg.warn(ClassSearcher.class, "Can't load extension point: " + className); Msg.warn(ClassSearcher.class, "Can't load extension point: " + className);
} }
} }
extensionPoints = Collections.unmodifiableSet(extensionClasses); extensionPoints = Collections.unmodifiableList(extensionClasses);
} }
catch (IOException e) { catch (IOException e) {

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,11 +15,6 @@
*/ */
package ghidra.util.classfinder; package ghidra.util.classfinder;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** /**
* NOTE: ExtensionPoint logistics have changed! It is no longer sufficient to * NOTE: ExtensionPoint logistics have changed! It is no longer sufficient to
* implement ExtensionPoint in order for the ClassSearcher to dynamically pick * implement ExtensionPoint in order for the ClassSearcher to dynamically pick
@ -35,15 +29,5 @@ import java.lang.annotation.Target;
* will automatically search for and load. * will automatically search for and load.
*/ */
public interface ExtensionPoint { public interface ExtensionPoint {
@Retention(RetentionPolicy.RUNTIME) // Marker interface
@Target(ElementType.TYPE)
public static @interface Exclude {
String reason();
}
public static class Util {
public static boolean isExcluded(Class<?> c) {
return c.getAnnotation(Exclude.class) != null;
}
}
} }

View file

@ -0,0 +1,83 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.util.classfinder;
import java.lang.annotation.*;
/**
* {@link ExtensionPoint} properties
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ExtensionPointProperties {
/**
* Default {@link ExtensionPoint} priority. Higher values represent higher priorities.
*/
final static int DEFAULT_PRIORITY = 1;
/**
* Default behavior for an {@link ExtensionPoint} being discoverable
*/
final static boolean DEFAULT_EXCLUDE = false;
/**
* {@link ExtensionPoint} priority. Higher values represent higher priorities.
*
* @return the {@link ExtensionPoint} priority.
*/
int priority() default DEFAULT_PRIORITY;
/**
* Enable to exclude an {@link ExtensionPoint} from being discovered
*
* @return true to exclude an {@link ExtensionPoint} from being discovered
*/
boolean exclude() default DEFAULT_EXCLUDE;
/**
* Utility methods for working with {@link ExtensionPointProperties}
*/
public static class Util {
/**
* Gets whether or not the {@link ExtensionPoint} will be excluded from being discovered
*
* @param c the class check
* @return true if the class is an {@link ExtensionPoint} and should be excluded from being
* discovered
*/
public static boolean isExcluded(Class<?> c) {
ExtensionPointProperties properties = c.getAnnotation(ExtensionPointProperties.class);
return properties != null ? properties.exclude()
: ExtensionPointProperties.DEFAULT_EXCLUDE;
}
/**
* Gets the {@link ExtensionPoint} priority.
*
* @param c the class to get {@link ExtensionPoint} priority of.
* @return the class's {@link ExtensionPoint} priority
* ({@link ExtensionPointProperties#DEFAULT_PRIORITY} will be used in a
* non-{@link ExtensionPoint} is passed in)
*/
public static int getPriority(Class<?> c) {
ExtensionPointProperties properties = c.getAnnotation(ExtensionPointProperties.class);
return properties != null ? properties.priority()
: ExtensionPointProperties.DEFAULT_PRIORITY;
}
}
}

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,12 +15,6 @@
*/ */
package ghidra.framework.data; package ghidra.framework.data;
import ghidra.framework.model.*;
import ghidra.framework.store.FileSystem;
import ghidra.framework.store.LockException;
import ghidra.util.Lock;
import ghidra.util.classfinder.ClassSearcher;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -29,6 +22,12 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
import ghidra.framework.model.*;
import ghidra.framework.store.FileSystem;
import ghidra.framework.store.LockException;
import ghidra.util.Lock;
import ghidra.util.classfinder.ClassSearcher;
/** /**
* An abstract class that provides default behavior for * An abstract class that provides default behavior for
* DomainObject(s), specifically it handles listeners and * DomainObject(s), specifically it handles listeners and
@ -488,7 +487,7 @@ public abstract class DomainObjectAdapter implements DomainObject {
contentHandlerClassMap = new HashMap<Class<?>, ContentHandler>(); contentHandlerClassMap = new HashMap<Class<?>, ContentHandler>();
contentHandlerTypeMap = new HashMap<String, ContentHandler>(); contentHandlerTypeMap = new HashMap<String, ContentHandler>();
Set<ContentHandler> handlers = ClassSearcher.getInstances(ContentHandler.class); List<ContentHandler> handlers = ClassSearcher.getInstances(ContentHandler.class);
for (ContentHandler ch : handlers) { for (ContentHandler ch : handlers) {
String type = ch.getContentType(); String type = ch.getContentType();
Class<?> DOClass = ch.getDomainObjectClass(); Class<?> DOClass = ch.getDomainObjectClass();

View file

@ -96,7 +96,7 @@ public class ProjectDataTableModel extends ThreadedTableModel<DomainFileInfo, Pr
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private List<ProjectDataColumn<?>> findAppSpecificColumns() { private List<ProjectDataColumn<?>> findAppSpecificColumns() {
Set<ProjectDataColumn> instances = ClassSearcher.getInstances(ProjectDataColumn.class); List<ProjectDataColumn> instances = ClassSearcher.getInstances(ProjectDataColumn.class);
List<ProjectDataColumn<?>> columns = new ArrayList<>(); List<ProjectDataColumn<?>> columns = new ArrayList<>();
for (ProjectDataColumn projectDataColumn : instances) { for (ProjectDataColumn projectDataColumn : instances) {

View file

@ -48,7 +48,7 @@ public class PluginClassManager {
(localExclusionClass == null || !localExclusionClass.isAssignableFrom(c)) && (localExclusionClass == null || !localExclusionClass.isAssignableFrom(c)) &&
!ProgramaticUseOnly.class.isAssignableFrom(c); !ProgramaticUseOnly.class.isAssignableFrom(c);
Set<Class<? extends Plugin>> classes = List<Class<? extends Plugin>> classes =
ClassSearcher.getClasses(Plugin.class, myClassFilter); ClassSearcher.getClasses(Plugin.class, myClassFilter);
for (Class<? extends Plugin> pluginClass : classes) { for (Class<? extends Plugin> pluginClass : classes) {

View file

@ -49,7 +49,8 @@ public abstract class PluginPackage implements ExtensionPoint, Comparable<Plugin
private static Map<String, PluginPackage> createPackageMap() { private static Map<String, PluginPackage> createPackageMap() {
Map<String, PluginPackage> map = new HashMap<>(); Map<String, PluginPackage> map = new HashMap<>();
Set<Class<? extends PluginPackage>> classes = ClassSearcher.getClasses(PluginPackage.class); List<Class<? extends PluginPackage>> classes =
ClassSearcher.getClasses(PluginPackage.class);
for (Class<? extends PluginPackage> class1 : classes) { for (Class<? extends PluginPackage> class1 : classes) {
PluginPackage pluginPackage; PluginPackage pluginPackage;
try { try {

View file

@ -119,7 +119,7 @@ public class PluginUtils {
// Now get all Plugin.class files that have been loaded, and see if any of them // Now get all Plugin.class files that have been loaded, and see if any of them
// were loaded from one of the jars we just found. // were loaded from one of the jars we just found.
Set<Class<? extends Plugin>> plugins = ClassSearcher.getClasses(Plugin.class); List<Class<? extends Plugin>> plugins = ClassSearcher.getClasses(Plugin.class);
for (Class<? extends Plugin> plugin : plugins) { for (Class<? extends Plugin> plugin : plugins) {
URL location = plugin.getResource('/' + plugin.getName().replace('.', '/') + ".class"); URL location = plugin.getResource('/' + plugin.getName().replace('.', '/') + ".class");
if (location == null) { if (location == null) {

View file

@ -390,7 +390,7 @@ class ToolServicesImpl implements ToolServices {
} }
contentHandlers = new HashSet<>(); contentHandlers = new HashSet<>();
Set<ContentHandler> instances = ClassSearcher.getInstances(ContentHandler.class); List<ContentHandler> instances = ClassSearcher.getInstances(ContentHandler.class);
for (ContentHandler contentHandler : instances) { for (ContentHandler contentHandler : instances) {
// a bit of validation // a bit of validation
String contentType = contentHandler.getContentType(); String contentType = contentHandler.getContentType();

View file

@ -128,7 +128,7 @@ public class Handler extends URLStreamHandler {
private static void loadGhidraProtocolHandlers() { private static void loadGhidraProtocolHandlers() {
protocolHandlers = new ArrayList<>(); protocolHandlers = new ArrayList<>();
Set<Class<? extends GhidraProtocolHandler>> classes = List<Class<? extends GhidraProtocolHandler>> classes =
ClassSearcher.getClasses(GhidraProtocolHandler.class); ClassSearcher.getClasses(GhidraProtocolHandler.class);
for (Class<?> c : classes) { for (Class<?> c : classes) {
try { try {

View file

@ -16,7 +16,7 @@
package ghidra.program.model.data; package ghidra.program.model.data;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Set; import java.util.List;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
@ -134,7 +134,7 @@ public class BuiltInDataTypeManager extends StandAloneDataTypeManager {
try { try {
ArrayList<DataType> list = new ArrayList<>(); ArrayList<DataType> list = new ArrayList<>();
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter(); ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
Set<BuiltInDataType> datatypes = List<BuiltInDataType> datatypes =
ClassSearcher.getInstances(BuiltInDataType.class, filter); ClassSearcher.getInstances(BuiltInDataType.class, filter);
for (BuiltInDataType datatype : datatypes) { for (BuiltInDataType datatype : datatypes) {
list.clear(); list.clear();

View file

@ -110,7 +110,7 @@ public class SymbolUtilities {
private static List<String> getDynamicDataTypePrefixes() { private static List<String> getDynamicDataTypePrefixes() {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
ClassFilter filter = new BuiltInDataTypeClassExclusionFilter(); ClassFilter filter = new BuiltInDataTypeClassExclusionFilter();
Set<BuiltInDataType> instances = ClassSearcher.getInstances(BuiltInDataType.class, filter); List<BuiltInDataType> instances = ClassSearcher.getInstances(BuiltInDataType.class, filter);
for (BuiltInDataType builtIn : instances) { for (BuiltInDataType builtIn : instances) {
String prefix = builtIn.getDefaultAbbreviatedLabelPrefix(); String prefix = builtIn.getDefaultAbbreviatedLabelPrefix();
if (prefix != null) { if (prefix != null) {

View file

@ -85,7 +85,7 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
} }
private void searchForProviders() { private void searchForProviders() {
Set<LanguageProvider> languageProviders = List<LanguageProvider> languageProviders =
ClassSearcher.getInstances(LanguageProvider.class); ClassSearcher.getInstances(LanguageProvider.class);
searchCompleted = true; searchCompleted = true;
@ -338,7 +338,7 @@ public class DefaultLanguageService implements LanguageService, ChangeListener {
throw new LanguageNotFoundException(processor); throw new LanguageNotFoundException(processor);
} }
private void processProviders(Set<LanguageProvider> providers) { private void processProviders(List<LanguageProvider> providers) {
for (LanguageProvider provider : providers) { for (LanguageProvider provider : providers) {
addLanguages(provider); addLanguages(provider);
} }

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,18 +15,18 @@
*/ */
package ghidra.util.constraint; package ghidra.util.constraint;
import java.util.List;
import generic.constraint.DecisionTree; import generic.constraint.DecisionTree;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.classfinder.ClassSearcher; import ghidra.util.classfinder.ClassSearcher;
import java.util.Set;
public class ProgramDecisionTree extends DecisionTree<Program> { public class ProgramDecisionTree extends DecisionTree<Program> {
public ProgramDecisionTree() { public ProgramDecisionTree() {
super(); super();
Set<Class<? extends ProgramConstraint>> classes = List<Class<? extends ProgramConstraint>> classes =
ClassSearcher.getClasses(ProgramConstraint.class); ClassSearcher.getClasses(ProgramConstraint.class);
for (Class<? extends ProgramConstraint> constraintClass : classes) { for (Class<? extends ProgramConstraint> constraintClass : classes) {
try { try {

View file

@ -22,6 +22,7 @@ import java.util.stream.Collectors;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.framework.GModule; import ghidra.framework.GModule;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import utility.application.ApplicationLayout;
import utility.module.ModuleUtilities; import utility.module.ModuleUtilities;
/** /**
@ -52,15 +53,17 @@ public class GhidraLauncher {
// Build the classpath // Build the classpath
List<String> classpathList = new ArrayList<String>(); List<String> classpathList = new ArrayList<String>();
Map<String, GModule> modules = getOrderedModules(layout);
if (SystemUtilities.isInDevelopmentMode()) { if (SystemUtilities.isInDevelopmentMode()) {
addModuleBinPaths(classpathList, layout.getModules()); addModuleBinPaths(classpathList, modules);
addExternalJarPaths(classpathList, layout.getApplicationRootDirs()); addExternalJarPaths(classpathList, layout.getApplicationRootDirs());
} }
else { else {
addPatchPaths(classpathList, layout.getApplicationInstallationDir()); addPatchPaths(classpathList, layout.getApplicationInstallationDir());
addModuleJarPaths(classpathList, layout.getModules()); addModuleJarPaths(classpathList, modules);
} }
classpathList = orderClasspath(classpathList, layout.getModules()); classpathList = orderClasspath(classpathList, modules);
// Add the classpath to the class loader // Add the classpath to the class loader
GhidraClassLoader loader = (GhidraClassLoader) ClassLoader.getSystemClassLoader(); GhidraClassLoader loader = (GhidraClassLoader) ClassLoader.getSystemClassLoader();
@ -189,6 +192,63 @@ public class GhidraLauncher {
return list; return list;
} }
/**
* Gets the modules ordered by "class-loader priority". This ensures that core modules (things
* in Framework/Features/Processors, etc) come before user modules (Extensions). It also
* guarantees a consistent module order from run to run.
*
* @param layout The layout
* @return the modules mapped by name, ordered by priority
*/
private static Map<String, GModule> getOrderedModules(ApplicationLayout layout) {
Comparator<GModule> comparator = (module1, module2) -> {
int nameComparison = module1.getName().compareTo(module2.getName());
// First handle modules that are external to the Ghidra installation.
// These should be put at the end of the list.
boolean external1 = ModuleUtilities.isExternalModule(module1, layout);
boolean external2 = ModuleUtilities.isExternalModule(module2, layout);
if (external1 && external2) {
return nameComparison;
}
if (external1) {
return -1;
}
if (external2) {
return 1;
}
// Now handle modules that are internal to the Ghidra installation.
// We will primarily order them by "type" and secondarily by name.
Map<String, Integer> typePriorityMap = Map.of(
"Framework", 0,
"Configurations", 1,
"Features", 2,
"Processors", 3,
"GPL", 4,
"Extensions", 5,
"Test", 6
);
String type1 = module1.getModuleRoot().getParentFile().getName();
String type2 = module2.getModuleRoot().getParentFile().getName();
int priority1 = typePriorityMap.getOrDefault(type1, typePriorityMap.size());
int priority2 = typePriorityMap.getOrDefault(type2, typePriorityMap.size());
if (priority1 != priority2) {
return Integer.compare(priority1, priority2);
}
return nameComparison;
};
List<GModule> moduleList = new ArrayList<>(layout.getModules().values());
Collections.sort(moduleList, comparator);
Map<String, GModule> moduleMap = new LinkedHashMap<>();
for (GModule module : moduleList) {
moduleMap.put(module.getName(), module);
}
return moduleMap;
}
/** /**
* Updates the list of paths to make sure the order is correct for any class-loading dependencies. * Updates the list of paths to make sure the order is correct for any class-loading dependencies.
* *
@ -199,18 +259,16 @@ public class GhidraLauncher {
private static List<String> orderClasspath(List<String> pathList, private static List<String> orderClasspath(List<String> pathList,
Map<String, GModule> modules) { Map<String, GModule> modules) {
//@formatter:off Set<String> fatJars = modules
Set<String> flatJars = modules
.values() .values()
.stream() .stream()
.flatMap(m -> m.getFatJars().stream()) .flatMap(m -> m.getFatJars().stream())
.collect(Collectors.toSet()); .collect(Collectors.toSet());
//@formatter:on
List<String> orderedList = new ArrayList<String>(pathList); List<String> orderedList = new ArrayList<String>(pathList);
for (String path : pathList) { for (String path : pathList) {
if (flatJars.contains(new File(path).getName())) { if (fatJars.contains(new File(path).getName())) {
orderedList.remove(path); orderedList.remove(path);
orderedList.add(path); orderedList.add(path);
} }

View file

@ -26,6 +26,7 @@ import ghidra.util.Msg;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
import utilities.util.FileUtilities; import utilities.util.FileUtilities;
import utility.application.ApplicationLayout;
/** /**
* Utility methods for module related things. * Utility methods for module related things.
@ -334,4 +335,21 @@ public class ModuleUtilities {
} }
return findRepo(f.getParentFile()); return findRepo(f.getParentFile());
} }
/**
* Checks to see if the given {@link GModule module} is external to the Ghidra installation
* directory
*
* @param module the module to check
* @param layout Ghidra's layout
* @return true if the given {@link GModule module} is external to the Ghidra installation
* directory
*/
public static boolean isExternalModule(GModule module, ApplicationLayout layout) {
File moduleRootDir = module.getModuleRoot().getFile(false);
return !layout.getApplicationRootDirs()
.stream()
.map(dir -> dir.getParentFile().getFile(false))
.anyMatch(dir -> FileUtilities.isPathContainedWithin(dir, moduleRootDir));
}
} }