From ed2d66e2bb900a1f6ba1b0345e36a78f6f2fdff9 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 6 Dec 2024 01:52:26 -0500 Subject: [PATCH] Refactor InheritanceGraph, TransformationApplier to also not be tied directly to CDI --- .../assembler/JvmAssemblerPipeline.java | 11 ++- .../services/callgraph/CallGraphService.java | 40 ++++---- .../StaticValueCollectionTransformer.java | 20 ++-- .../inheritance/InheritanceGraph.java | 17 +--- .../inheritance/InheritanceGraphConfig.java | 2 +- .../inheritance/InheritanceGraphService.java | 96 ++++++++++++++++++ .../services/mapping/MappingApplier.java | 6 +- .../services/mapping/MappingListeners.java | 3 +- .../services/mapping/MappingsAdapter.java | 14 +-- .../transform/JvmTransformerContext.java | 6 +- .../transform/TransformationApplier.java | 61 +++++------- .../TransformationApplierConfig.java | 4 +- .../TransformationApplierService.java | 98 +++++++++++++++++++ .../ThrowablePropertyAssigningProcessor.java | 41 ++++---- .../software/coley/recaf/BootstrapTest.java | 19 ++-- .../services/callgraph/CallGraphTest.java | 1 + .../DeobfuscationTransformTest.java | 11 ++- .../InheritanceAndRenamingTest.java | 3 +- .../inheritance/InheritanceGraphTest.java | 2 +- .../services/mapping/MappingApplierTest.java | 5 +- .../mapping/gen/MappingGeneratorTest.java | 3 +- .../transform/TransformationApplierTest.java | 35 +++---- .../control/graph/MethodCallGraphsPane.java | 2 +- .../recaf/ui/pane/MappingGeneratorPane.java | 52 ++++++++-- .../pane/editing/assembler/AssemblerPane.java | 4 +- .../ui/pane/editing/tabs/InheritancePane.java | 5 +- 26 files changed, 396 insertions(+), 165 deletions(-) create mode 100644 recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphService.java create mode 100644 recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierService.java diff --git a/recaf-core/src/main/java/software/coley/recaf/services/assembler/JvmAssemblerPipeline.java b/recaf-core/src/main/java/software/coley/recaf/services/assembler/JvmAssemblerPipeline.java index 2bd2b2fdc..f9377d03a 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/assembler/JvmAssemblerPipeline.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/assembler/JvmAssemblerPipeline.java @@ -29,13 +29,14 @@ import software.coley.recaf.path.ClassPathNode; import software.coley.recaf.path.PathNode; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.inheritance.InheritanceVertex; import software.coley.recaf.util.JavaVersion; import software.coley.recaf.workspace.model.Workspace; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.util.List; +import java.util.Objects; /** * JVM assembler pipeline implementation. @@ -52,12 +53,12 @@ public class JvmAssemblerPipeline extends AbstractAssemblerPipeline classValues = new ConcurrentHashMap<>(); private final Map classFinals = new ConcurrentHashMap<>(); - private final InheritanceGraph graph; + private final Map graphCache = new IdentityHashMap<>(); + private final InheritanceGraphService graphService; + private final WorkspaceManager workspaceManager; @Inject - public StaticValueCollectionTransformer(@Nonnull InheritanceGraph graph) { - this.graph = graph; + public StaticValueCollectionTransformer(@Nonnull WorkspaceManager workspaceManager, @Nonnull InheritanceGraphService graphService) { + this.workspaceManager = workspaceManager; + this.graphService = graphService; } @Nullable @@ -67,6 +70,11 @@ public ReValue getStaticValue(@Nonnull String className, @Nonnull String fieldNa public void transform(@Nonnull JvmTransformerContext context, @Nonnull Workspace workspace, @Nonnull WorkspaceResource resource, @Nonnull JvmClassBundle bundle, @Nonnull JvmClassInfo classInfo) throws TransformationException { + // TODO: Instead of a map, we should make a workspace setup call first + InheritanceGraph graph = graphCache.computeIfAbsent(workspace, w -> w == workspaceManager.getCurrent() ? + graphService.getCurrentWorkspaceInheritanceGraph() : + graphService.newInheritanceGraph(workspace)); + StaticValues valuesContainer = new StaticValues(); EffectivelyFinalFields finalContainer = new EffectivelyFinalFields(); diff --git a/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraph.java b/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraph.java index c09e5332d..b00d7126d 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraph.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraph.java @@ -2,11 +2,7 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import jakarta.inject.Inject; import software.coley.collections.Lists; -import software.coley.recaf.cdi.AutoRegisterWorkspaceListeners; -import software.coley.recaf.cdi.EagerInitialization; -import software.coley.recaf.cdi.WorkspaceScoped; import software.coley.recaf.info.AndroidClassInfo; import software.coley.recaf.info.BasicJvmClassInfo; import software.coley.recaf.info.ClassInfo; @@ -16,7 +12,6 @@ import software.coley.recaf.path.ResourcePathNode; import software.coley.recaf.services.Service; import software.coley.recaf.services.mapping.MappingApplicationListener; -import software.coley.recaf.services.mapping.MappingListeners; import software.coley.recaf.services.mapping.MappingResults; import software.coley.recaf.services.workspace.WorkspaceCloseListener; import software.coley.recaf.workspace.model.Workspace; @@ -42,13 +37,10 @@ import java.util.stream.Stream; /** - * Class inheritance graph utility. + * Represents class inheritance as a navigable graph. * * @author Matt Coley */ -@WorkspaceScoped -@EagerInitialization -@AutoRegisterWorkspaceListeners public class InheritanceGraph implements Service, WorkspaceModificationListener, WorkspaceCloseListener, ResourceJvmClassListener, ResourceAndroidClassListener, MappingApplicationListener { public static final String SERVICE_ID = "graph-inheritance"; @@ -70,8 +62,8 @@ public class InheritanceGraph implements Service, WorkspaceModificationListener, * @param workspace * Workspace to pull classes from. */ - @Inject - public InheritanceGraph(@Nonnull InheritanceGraphConfig config, @Nonnull MappingListeners mappingListeners, @Nonnull Workspace workspace) { + public InheritanceGraph(@Nonnull InheritanceGraphConfig config, + @Nonnull Workspace workspace) { this.config = config; this.workspace = workspace; @@ -80,9 +72,6 @@ public InheritanceGraph(@Nonnull InheritanceGraphConfig config, @Nonnull Mapping primaryResource.addResourceJvmClassListener(this); primaryResource.addResourceAndroidClassListener(this); - // Add listener to handle updating the graph when renaming is applied. - mappingListeners.addMappingApplicationListener(this); - // Populate downwards (parent --> child) lookup refreshChildLookup(); } diff --git a/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphConfig.java b/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphConfig.java index e9297f54b..85dda9a3c 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphConfig.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphConfig.java @@ -7,7 +7,7 @@ import software.coley.recaf.services.ServiceConfig; /** - * Config for {@link InheritanceGraph} + * Config for {@link InheritanceGraphService} * * @author Matt Coley */ diff --git a/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphService.java b/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphService.java new file mode 100644 index 000000000..ad7efd1e4 --- /dev/null +++ b/recaf-core/src/main/java/software/coley/recaf/services/inheritance/InheritanceGraphService.java @@ -0,0 +1,96 @@ +package software.coley.recaf.services.inheritance; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import software.coley.recaf.cdi.EagerInitialization; +import software.coley.recaf.services.Service; +import software.coley.recaf.services.mapping.MappingListeners; +import software.coley.recaf.services.workspace.WorkspaceCloseListener; +import software.coley.recaf.services.workspace.WorkspaceManager; +import software.coley.recaf.workspace.model.Workspace; + +/** + * Service offering the creation of {@link InheritanceGraph inheritance graphs} for workspaces. + * + * @author Matt Coley + * @see InheritanceGraph + */ +@EagerInitialization +@ApplicationScoped +public class InheritanceGraphService implements Service { + public static final String SERVICE_ID = "graph-inheritance"; + private final InheritanceGraphConfig config; + private final MappingListeners mappingListeners; + private final WorkspaceManager workspaceManager; + private InheritanceGraph currentWorkspaceGraph; + + @Inject + public InheritanceGraphService(@Nonnull WorkspaceManager workspaceManager, + @Nonnull MappingListeners mappingListeners, + @Nonnull InheritanceGraphConfig config) { + this.workspaceManager = workspaceManager; + this.mappingListeners = mappingListeners; + this.config = config; + + ListenerHost host = new ListenerHost(); + workspaceManager.addWorkspaceCloseListener(host); + } + + /** + * @param workspace + * Workspace to pull classes from. + * + * @return New inheritance graph model for the given workspace. + */ + @Nonnull + public InheritanceGraph newInheritanceGraph(@Nonnull Workspace workspace) { + InheritanceGraph graph = new InheritanceGraph(config, workspace); + workspace.addWorkspaceModificationListener(graph); + return graph; + } + + /** + * @return Inheritance graph model for the {@link WorkspaceManager#getCurrent() current workspace} + * or {@code null} if no workspace is currently open. + */ + @Nullable + public InheritanceGraph getCurrentWorkspaceInheritanceGraph() { + Workspace workspace = workspaceManager.getCurrent(); + if (workspace == null) + return null; + + if (currentWorkspaceGraph == null) { + InheritanceGraph graph = newInheritanceGraph(workspace); + currentWorkspaceGraph = graph; + + // Add listener to handle updating the graph when renaming is applied to the current workspace. + mappingListeners.addMappingApplicationListener(graph); + } + + return currentWorkspaceGraph; + } + + @Nonnull + @Override + public String getServiceId() { + return SERVICE_ID; + } + + @Nonnull + @Override + public InheritanceGraphConfig getServiceConfig() { + return config; + } + + private class ListenerHost implements WorkspaceCloseListener { + @Override + public void onWorkspaceClosed(@Nonnull Workspace workspace) { + if (currentWorkspaceGraph != null) { + currentWorkspaceGraph.onWorkspaceClosed(workspace); + currentWorkspaceGraph = null; + } + } + } +} \ No newline at end of file diff --git a/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingApplier.java b/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingApplier.java index c9e740be8..260d5564b 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingApplier.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingApplier.java @@ -12,6 +12,7 @@ import software.coley.recaf.info.properties.builtin.RemapOriginTaskProperty; import software.coley.recaf.services.Service; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.mapping.aggregate.AggregateMappingManager; import software.coley.recaf.util.threading.ThreadPoolFactory; import software.coley.recaf.util.threading.ThreadUtil; @@ -21,6 +22,7 @@ import software.coley.recaf.workspace.model.resource.WorkspaceResource; import java.util.Collection; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.stream.Stream; @@ -43,11 +45,11 @@ public class MappingApplier implements Service { @Inject public MappingApplier(@Nonnull MappingApplierConfig config, - @Nonnull InheritanceGraph inheritanceGraph, + @Nonnull InheritanceGraphService graphService, @Nonnull AggregateMappingManager aggregateMappingManager, @Nonnull MappingListeners listeners, @Nonnull Workspace workspace) { - this.inheritanceGraph = inheritanceGraph; + this.inheritanceGraph = Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created"); this.aggregateMappingManager = aggregateMappingManager; this.listeners = listeners; this.workspace = workspace; diff --git a/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingListeners.java b/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingListeners.java index 1e6999c32..00649e41d 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingListeners.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingListeners.java @@ -11,6 +11,7 @@ import software.coley.recaf.workspace.model.bundle.JvmClassBundle; import software.coley.recaf.workspace.model.resource.WorkspaceResource; +import java.util.Collection; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -34,7 +35,7 @@ public MappingListeners(@Nonnull MappingListenersConfig config) { /** * Adds a listener which is passed to created {@link MappingResults} from * {@link MappingApplier#applyToPrimaryResource(Mappings)} and - * {@link MappingApplier#applyToClasses(Mappings, WorkspaceResource, JvmClassBundle, List)}. + * {@link MappingApplier#applyToClasses(Mappings, WorkspaceResource, JvmClassBundle, Collection)}. *

* This allows you to listen to all mapping operations done via proper API usage, intercepting before they * execute the task, and after they complete the mapping task. diff --git a/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingsAdapter.java b/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingsAdapter.java index 2543ccced..80ae49978 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingsAdapter.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/mapping/MappingsAdapter.java @@ -31,7 +31,7 @@ public class MappingsAdapter implements Mappings { private final Map mappings = new HashMap<>(); private final boolean supportFieldTypeDifferentiation; private final boolean supportVariableTypeDifferentiation; - private InheritanceGraph graph; + private InheritanceGraph inheritanceGraph; private Workspace workspace; /** @@ -130,7 +130,7 @@ public String getMappedClassName(@Nonnull String internalName) { public String getMappedFieldName(@Nonnull String ownerName, @Nonnull String fieldName, @Nonnull String fieldDesc) { MappingKey key = getFieldKey(ownerName, fieldName, fieldDesc); String mapped = mappings.get(key); - if (mapped == null && graph != null) { + if (mapped == null && inheritanceGraph != null) { mapped = findInParent(ownerName, parent -> getFieldKey(parent, fieldName, fieldDesc)); } return mapped; @@ -141,7 +141,7 @@ public String getMappedFieldName(@Nonnull String ownerName, @Nonnull String fiel public String getMappedMethodName(@Nonnull String ownerName, @Nonnull String methodName, @Nonnull String methodDesc) { MappingKey key = getMethodKey(ownerName, methodName, methodDesc); String mapped = mappings.get(key); - if (mapped == null && graph != null) { + if (mapped == null && inheritanceGraph != null) { mapped = findInParent(ownerName, parent -> getMethodKey(parent, methodName, methodDesc)); } return mapped; @@ -198,7 +198,7 @@ public boolean doesSupportVariableTypeDifferentiation() { * @return The first mapping match in a parent class found by the lookup function. */ private String findInParent(String owner, Function lookup) { - InheritanceVertex vertex = graph.getVertex(owner); + InheritanceVertex vertex = inheritanceGraph.getVertex(owner); if (vertex == null) return null; Iterator iterator = vertex.allParents().iterator(); @@ -232,11 +232,11 @@ private boolean isInner(String internalName) { * was to {@link TreeMap#size()}. If you only have the {@link Map} entry you need the type hierarchy to find that * {@link TreeMap} is a child of {@link Map} and thus should "inherit" the mapping of {@link Map#size()}. * - * @param graph + * @param inheritanceGraph * Inheritance graph to use. */ - public void enableHierarchyLookup(@Nonnull InheritanceGraph graph) { - this.graph = graph; + public void enableHierarchyLookup(@Nonnull InheritanceGraph inheritanceGraph) { + this.inheritanceGraph = inheritanceGraph; } /** diff --git a/recaf-core/src/main/java/software/coley/recaf/services/transform/JvmTransformerContext.java b/recaf-core/src/main/java/software/coley/recaf/services/transform/JvmTransformerContext.java index 5f1065621..2f40cfebb 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/transform/JvmTransformerContext.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/transform/JvmTransformerContext.java @@ -66,18 +66,18 @@ public JvmTransformerContext(@Nonnull Workspace workspace, @Nonnull WorkspaceRes /** * Builds the map of initial transformed class paths to their final transformed states. * - * @param graph + * @param inheritanceGraph * Inheritance graph tied to the workspace the transformed classes belong to. */ @Nonnull - protected Map buildChangeMap(@Nonnull InheritanceGraph graph) { + protected Map buildChangeMap(@Nonnull InheritanceGraph inheritanceGraph) { ResourcePathNode resourcePath = PathNodes.resourcePath(workspace, resource); Map map = new HashMap<>(); for (JvmClassData data : classData.values()) { if (data.isDirty()) { if (data.node != null) { // Emit bytecode from the current node - ClassWriter writer = new WorkspaceClassWriter(graph, data.initialClass.getClassReader(), 0); + ClassWriter writer = new WorkspaceClassWriter(inheritanceGraph, data.initialClass.getClassReader(), 0); data.node.accept(writer); byte[] modifiedBytes = writer.toByteArray(); diff --git a/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplier.java b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplier.java index e0c61e846..ebf237b66 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplier.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplier.java @@ -2,18 +2,15 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import jakarta.inject.Inject; import org.slf4j.Logger; import software.coley.collections.Sets; import software.coley.collections.Unchecked; import software.coley.recaf.analytics.logging.Logging; -import software.coley.recaf.cdi.WorkspaceScoped; import software.coley.recaf.info.JvmClassInfo; import software.coley.recaf.path.BundlePathNode; import software.coley.recaf.path.ClassPathNode; import software.coley.recaf.path.PathNodes; import software.coley.recaf.path.ResourcePathNode; -import software.coley.recaf.services.Service; import software.coley.recaf.services.inheritance.InheritanceGraph; import software.coley.recaf.workspace.model.Workspace; import software.coley.recaf.workspace.model.bundle.JvmClassBundle; @@ -32,25 +29,29 @@ * @author Matt Coley * @see TransformationManager */ -@WorkspaceScoped -public class TransformationApplier implements Service { - public static final String SERVICE_ID = "transformation-applier"; +public class TransformationApplier { private static final Logger logger = Logging.get(TransformationApplier.class); - private final TransformationManager manager; - private final InheritanceGraph graph; - private final TransformationApplierConfig config; - - @Inject - public TransformationApplier(@Nonnull TransformationManager manager, @Nonnull InheritanceGraph graph, - @Nonnull TransformationApplierConfig config) { - this.manager = manager; - this.graph = graph; - this.config = config; - } + private final TransformationManager transformationManager; + private final InheritanceGraph inheritanceGraph; + private final Workspace workspace; /** + * @param transformationManager + * Manager to pull transformer instances from. + * @param inheritanceGraph + * Inheritance graph to use for frame computation (Some transformers will trigger this) * @param workspace - * Workspace to transform. + * Workspace with classes to transform. + */ + public TransformationApplier(@Nonnull TransformationManager transformationManager, + @Nonnull InheritanceGraph inheritanceGraph, + @Nonnull Workspace workspace) { + this.transformationManager = transformationManager; + this.workspace = workspace; + this.inheritanceGraph = inheritanceGraph; + } + + /** * @param transformerClasses * JVM class transformers to run. * @@ -61,13 +62,11 @@ public TransformationApplier(@Nonnull TransformationManager manager, @Nonnull In * When transformation cannot be run for any reason. */ @Nonnull - public TransformResult transformJvm(@Nonnull Workspace workspace, @Nonnull List> transformerClasses) throws TransformationException { - return transformJvm(workspace, transformerClasses, null); + public TransformResult transformJvm(@Nonnull List> transformerClasses) throws TransformationException { + return transformJvm(transformerClasses, null); } /** - * @param workspace - * Workspace to transform. * @param transformerClasses * JVM class transformers to run. * @param predicate @@ -81,7 +80,7 @@ public TransformResult transformJvm(@Nonnull Workspace workspace, @Nonnull List< * When transformation cannot be run for any reason. */ @Nonnull - public TransformResult transformJvm(@Nonnull Workspace workspace, @Nonnull List> transformerClasses, + public TransformResult transformJvm(@Nonnull List> transformerClasses, @Nullable JvmClassTransformerPredicate predicate) throws TransformationException { // Build transformer visitation order TransformerQueue queue = buildQueue(transformerClasses); @@ -114,7 +113,7 @@ public TransformResult transformJvm(@Nonnull Workspace workspace, @Nonnull List< }); // Update the workspace contents with the transformation results - Map transformedJvmClasses = context.buildChangeMap(graph); + Map transformedJvmClasses = context.buildChangeMap(inheritanceGraph); return new TransformResult() { @Nonnull @Override @@ -161,25 +160,13 @@ private void insert(@Nonnull TransformerQueue queue, @Nonnull Class dependency : transformer.dependencies()) if (!queue.containsType(dependency)) insert(queue, dependency, Sets.add(dependants, transformerClass)); queue.add(transformer); } - @Nonnull - @Override - public String getServiceId() { - return SERVICE_ID; - } - - @Nonnull - @Override - public TransformationApplierConfig getServiceConfig() { - return config; - } - /** * Wrapper holding which transformers to run. */ diff --git a/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierConfig.java b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierConfig.java index 0962b1122..050ac9294 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierConfig.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierConfig.java @@ -7,7 +7,7 @@ import software.coley.recaf.services.ServiceConfig; /** - * Config for {@link TransformationApplier}. + * Config for {@link TransformationApplierService}. * * @author Matt Coley */ @@ -15,6 +15,6 @@ public class TransformationApplierConfig extends BasicConfigContainer implements ServiceConfig { @Inject public TransformationApplierConfig() { - super(ConfigGroups.SERVICE_TRANSFORM, TransformationApplier.SERVICE_ID + CONFIG_SUFFIX); + super(ConfigGroups.SERVICE_TRANSFORM, TransformationApplierService.SERVICE_ID + CONFIG_SUFFIX); } } diff --git a/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierService.java b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierService.java new file mode 100644 index 000000000..0144e8b14 --- /dev/null +++ b/recaf-core/src/main/java/software/coley/recaf/services/transform/TransformationApplierService.java @@ -0,0 +1,98 @@ +package software.coley.recaf.services.transform; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.slf4j.Logger; +import software.coley.recaf.analytics.logging.Logging; +import software.coley.recaf.services.Service; +import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; +import software.coley.recaf.services.workspace.WorkspaceManager; +import software.coley.recaf.workspace.model.Workspace; + +import java.util.Objects; + +/** + * Service offering the creation of {@link TransformationApplier transformation appliers} for workspaces. + * + * @author Matt Coley + * @see TransformationManager + * @see TransformationApplier + */ +@ApplicationScoped +public class TransformationApplierService implements Service { + public static final String SERVICE_ID = "transformation-applier"; + private static final Logger logger = Logging.get(TransformationApplierService.class); + private final TransformationManager transformationManager; + private final InheritanceGraphService graphService; + private final WorkspaceManager workspaceManager; + private final TransformationApplierConfig config; + + @Inject + public TransformationApplierService(@Nonnull TransformationManager transformationManager, + @Nonnull InheritanceGraphService graphService, + @Nonnull WorkspaceManager workspaceManager, + @Nonnull TransformationApplierConfig config) { + this.graphService = graphService; + this.workspaceManager = workspaceManager; + this.transformationManager = transformationManager; + this.config = config; + } + + /** + * @param workspace + * Workspace to apply transformations within. + * + * @return Transformation applier for the given workspace. + */ + @Nonnull + public TransformationApplier newApplier(@Nonnull Workspace workspace) { + // Optimal case for current workspace using the shared workspace inheritance graph + if (workspace == workspaceManager.getCurrent()) + return newApplier(workspace, Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created")); + + // Need to make a new graph for the given workspace + InheritanceGraph graph = graphService.newInheritanceGraph(workspace); + return newApplier(workspace, graph); + } + + /** + * @return Transformation applier for the {@link WorkspaceManager#getCurrent() current workspace} + * or {@code null} if no workspace is currently open. + */ + @Nullable + public TransformationApplier newApplierForCurrentWorkspace() { + Workspace workspace = workspaceManager.getCurrent(); + if (workspace == null) + return null; + InheritanceGraph graph = Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created"); + return newApplier(workspace, graph); + } + + /** + * @param workspace + * Workspace to apply transformations within. + * @param inheritanceGraph + * Inheritance graph for the given workspace. + * + * @return Transformation applier for the given workspace. + */ + @Nonnull + private TransformationApplier newApplier(@Nonnull Workspace workspace, @Nonnull InheritanceGraph inheritanceGraph) { + return new TransformationApplier(transformationManager, inheritanceGraph, workspace); + } + + @Nonnull + @Override + public String getServiceId() { + return SERVICE_ID; + } + + @Nonnull + @Override + public TransformationApplierConfig getServiceConfig() { + return config; + } +} diff --git a/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java b/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java index a92c104cf..5813cc4a9 100644 --- a/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java +++ b/recaf-core/src/main/java/software/coley/recaf/services/workspace/processors/ThrowablePropertyAssigningProcessor.java @@ -1,23 +1,26 @@ package software.coley.recaf.services.workspace.processors; import jakarta.annotation.Nonnull; +import jakarta.enterprise.context.Dependent; import jakarta.inject.Inject; -import software.coley.recaf.cdi.WorkspaceScoped; import software.coley.recaf.info.AndroidClassInfo; import software.coley.recaf.info.ClassInfo; import software.coley.recaf.info.JvmClassInfo; import software.coley.recaf.info.properties.builtin.ThrowableProperty; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.inheritance.InheritanceVertex; -import software.coley.recaf.workspace.model.WorkspaceModificationListener; import software.coley.recaf.services.workspace.WorkspaceProcessor; import software.coley.recaf.workspace.model.Workspace; +import software.coley.recaf.workspace.model.WorkspaceModificationListener; import software.coley.recaf.workspace.model.bundle.AndroidClassBundle; import software.coley.recaf.workspace.model.bundle.JvmClassBundle; import software.coley.recaf.workspace.model.resource.ResourceAndroidClassListener; import software.coley.recaf.workspace.model.resource.ResourceJvmClassListener; import software.coley.recaf.workspace.model.resource.WorkspaceResource; +import java.util.Objects; + /** * Workspace processor that marks {@link ClassInfo} values that inherit from {@link Throwable} * as having a {@link ThrowableProperty}. This allows instant look-ups for if a class is throwable, @@ -25,14 +28,14 @@ * * @author Matt Coley */ -@WorkspaceScoped +@Dependent public class ThrowablePropertyAssigningProcessor implements WorkspaceProcessor, ResourceJvmClassListener, ResourceAndroidClassListener { private static final String THROWABLE = "java/lang/Throwable"; private final InheritanceGraph graph; @Inject - public ThrowablePropertyAssigningProcessor(InheritanceGraph graph) { - this.graph = graph; + public ThrowablePropertyAssigningProcessor(@Nonnull InheritanceGraphService graphService) { + this.graph = Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created"); } @Override @@ -74,45 +77,45 @@ private void handle(@Nonnull ClassInfo cls) { @Override public void onNewClass(@Nonnull WorkspaceResource resource, - @Nonnull AndroidClassBundle bundle, - @Nonnull AndroidClassInfo cls) { + @Nonnull AndroidClassBundle bundle, + @Nonnull AndroidClassInfo cls) { handle(cls); } @Override public void onUpdateClass(@Nonnull WorkspaceResource resource, - @Nonnull AndroidClassBundle bundle, - @Nonnull AndroidClassInfo oldCls, - @Nonnull AndroidClassInfo newCls) { + @Nonnull AndroidClassBundle bundle, + @Nonnull AndroidClassInfo oldCls, + @Nonnull AndroidClassInfo newCls) { handle(newCls); } @Override public void onRemoveClass(@Nonnull WorkspaceResource resource, - @Nonnull AndroidClassBundle bundle, - @Nonnull AndroidClassInfo cls) { + @Nonnull AndroidClassBundle bundle, + @Nonnull AndroidClassInfo cls) { // no-op } @Override public void onNewClass(@Nonnull WorkspaceResource resource, - @Nonnull JvmClassBundle bundle, - @Nonnull JvmClassInfo cls) { + @Nonnull JvmClassBundle bundle, + @Nonnull JvmClassInfo cls) { handle(cls); } @Override public void onUpdateClass(@Nonnull WorkspaceResource resource, - @Nonnull JvmClassBundle bundle, - @Nonnull JvmClassInfo oldCls, - @Nonnull JvmClassInfo newCls) { + @Nonnull JvmClassBundle bundle, + @Nonnull JvmClassInfo oldCls, + @Nonnull JvmClassInfo newCls) { handle(newCls); } @Override public void onRemoveClass(@Nonnull WorkspaceResource resource, - @Nonnull JvmClassBundle bundle, - @Nonnull JvmClassInfo cls) { + @Nonnull JvmClassBundle bundle, + @Nonnull JvmClassInfo cls) { // no-op } } diff --git a/recaf-core/src/test/java/software/coley/recaf/BootstrapTest.java b/recaf-core/src/test/java/software/coley/recaf/BootstrapTest.java index 590586b2e..bdfae0627 100644 --- a/recaf-core/src/test/java/software/coley/recaf/BootstrapTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/BootstrapTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.mapping.aggregate.AggregateMappingManager; import software.coley.recaf.test.TestBase; import software.coley.recaf.test.TestClassUtils; import software.coley.recaf.test.dummy.HelloWorld; @@ -57,18 +58,18 @@ void testGetWorkspaceInstance() throws IOException { @Test void testGetWorkspaceScopedInstance() { - // Get the graph when one workspace is open. + // Get the manager when one workspace is open. workspaceManager.setCurrent(EmptyWorkspace.get()); - InheritanceGraph graph1 = unwrapProxy(recaf.get(InheritanceGraph.class)); - InheritanceGraph graph2 = unwrapProxy(recaf.get(InheritanceGraph.class)); - assertSame(graph1, graph2, "Graph should be workspace-scoped, but values differ!"); + AggregateMappingManager manager1 = unwrapProxy(recaf.get(AggregateMappingManager.class)); + AggregateMappingManager manager2 = unwrapProxy(recaf.get(AggregateMappingManager.class)); + assertSame(manager1, manager2, "Graph should be workspace-scoped, but values differ!"); // Assign a new workspace. - // The graph should be different since the prior workspace is closed. + // The manager should be different since the prior workspace is closed. workspaceManager.setCurrent(EmptyWorkspace.get()); - InheritanceGraph graph3 = unwrapProxy(recaf.get(InheritanceGraph.class)); - InheritanceGraph graph4 = unwrapProxy(recaf.get(InheritanceGraph.class)); - assertSame(graph3, graph4, "Graph should be workspace-scoped, but values differ!"); - assertNotSame(graph1, graph3, "Graph scope from before/after a new workspace yielded the same graph bean!"); + AggregateMappingManager manager3 = unwrapProxy(recaf.get(AggregateMappingManager.class)); + AggregateMappingManager manager4 = unwrapProxy(recaf.get(AggregateMappingManager.class)); + assertSame(manager3, manager4, "Graph should be workspace-scoped, but values differ!"); + assertNotSame(manager1, manager3, "Graph scope from before/after a new workspace yielded the same graph bean!"); } } diff --git a/recaf-core/src/test/java/software/coley/recaf/services/callgraph/CallGraphTest.java b/recaf-core/src/test/java/software/coley/recaf/services/callgraph/CallGraphTest.java index 4308b081b..56cb4b6f8 100644 --- a/recaf-core/src/test/java/software/coley/recaf/services/callgraph/CallGraphTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/services/callgraph/CallGraphTest.java @@ -96,6 +96,7 @@ void testUnresolvedCall() { @Nonnull static CallGraph graph(@Nonnull Workspace workspace) { CallGraph graph = new CallGraph(workspace); + graph.initialize(); // Need to wait until async population of graph contents is done. ObservableBoolean ready = graph.isReady(); diff --git a/recaf-core/src/test/java/software/coley/recaf/services/deobfuscation/DeobfuscationTransformTest.java b/recaf-core/src/test/java/software/coley/recaf/services/deobfuscation/DeobfuscationTransformTest.java index 360b06acb..857e5dae3 100644 --- a/recaf-core/src/test/java/software/coley/recaf/services/deobfuscation/DeobfuscationTransformTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/services/deobfuscation/DeobfuscationTransformTest.java @@ -23,6 +23,7 @@ import software.coley.recaf.services.deobfuscation.builtin.StaticValueInliningTransformer; import software.coley.recaf.services.transform.TransformResult; import software.coley.recaf.services.transform.TransformationApplier; +import software.coley.recaf.services.transform.TransformationApplierService; import software.coley.recaf.test.TestBase; import software.coley.recaf.workspace.model.BasicWorkspace; import software.coley.recaf.workspace.model.Workspace; @@ -42,14 +43,14 @@ class DeobfuscationTransformTest extends TestBase { private static final boolean PRINT_BEFORE_AFTER = true; private static final String CLASS_NAME = "Example"; private static JvmAssemblerPipeline assembler; + private static TransformationApplierService transformationApplierService; private static TransformationApplier transformationApplier; private static JvmDecompiler decompiler; private static Workspace workspace; @BeforeAll static void setupServices() { - assembler = recaf.get(JvmAssemblerPipeline.class); - transformationApplier = recaf.get(TransformationApplier.class); + transformationApplierService = recaf.get(TransformationApplierService.class); decompiler = new CfrDecompiler(new CfrConfig()); } @@ -57,6 +58,8 @@ static void setupServices() { void setupWorkspace() { workspace = new BasicWorkspace(new WorkspaceResourceBuilder().build()); workspaceManager.setCurrentIgnoringConditions(workspace); + assembler = recaf.get(JvmAssemblerPipeline.class); + transformationApplier = transformationApplierService.newApplierForCurrentWorkspace(); } @Nested @@ -279,7 +282,7 @@ private void validateNoTransformation(@Nonnull String assembly) { JvmClassInfo cls = assemble(assembly); // Transforming should not actually result in any changes - TransformResult result = assertDoesNotThrow(() -> transformationApplier.transformJvm(workspace, List.of(StaticValueInliningTransformer.class))); + TransformResult result = assertDoesNotThrow(() -> transformationApplier.transformJvm( List.of(StaticValueInliningTransformer.class))); assertTrue(result.getJvmTransformerFailures().isEmpty(), "There were transformation failures"); assertEquals(0, result.getJvmTransformedClasses().size(), "There were unexpected transformations applied"); } @@ -293,7 +296,7 @@ private void validateBeforeAfter(@Nonnull String assembly, @Nonnull String expec assertTrue(initialDecompile.contains(expectedBefore)); // Run the transformer - TransformResult result = assertDoesNotThrow(() -> transformationApplier.transformJvm(workspace, List.of(StaticValueInliningTransformer.class))); + TransformResult result = assertDoesNotThrow(() -> transformationApplier.transformJvm( List.of(StaticValueInliningTransformer.class))); assertTrue(result.getJvmTransformerFailures().isEmpty(), "There were transformation failures"); assertEquals(1, result.getJvmTransformedClasses().size(), "Expected transformation to be applied"); diff --git a/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceAndRenamingTest.java b/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceAndRenamingTest.java index 2e32fce29..6def1c867 100644 --- a/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceAndRenamingTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceAndRenamingTest.java @@ -29,7 +29,6 @@ class InheritanceAndRenamingTest extends TestBase { static MappingApplier mappingApplier; static JvmClassInfo[] generatedClasses; - @BeforeAll static void setup() { generatedClasses = IntStream.rangeClosed(1, 5).mapToObj(i -> { @@ -45,7 +44,7 @@ static void setup() { workspaceManager.setCurrent(workspace); // Get graph - graph = recaf.get(InheritanceGraph.class); + graph = recaf.get(InheritanceGraphService.class).getCurrentWorkspaceInheritanceGraph(); graph.toString(); // Force immediate init. // Get mapping applier diff --git a/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceGraphTest.java b/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceGraphTest.java index 0018afba2..27ad3d4e5 100644 --- a/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceGraphTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/services/inheritance/InheritanceGraphTest.java @@ -38,7 +38,7 @@ static void setup() throws IOException { workspaceManager.setCurrent(workspace); // Get graph - graph = recaf.get(InheritanceGraph.class); + graph = recaf.get(InheritanceGraphService.class).getCurrentWorkspaceInheritanceGraph(); } @Test diff --git a/recaf-core/src/test/java/software/coley/recaf/services/mapping/MappingApplierTest.java b/recaf-core/src/test/java/software/coley/recaf/services/mapping/MappingApplierTest.java index 01aeb18c7..57468c403 100644 --- a/recaf-core/src/test/java/software/coley/recaf/services/mapping/MappingApplierTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/services/mapping/MappingApplierTest.java @@ -12,6 +12,7 @@ import software.coley.recaf.info.properties.builtin.OriginalClassNameProperty; import software.coley.recaf.path.ClassPathNode; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.mapping.aggregate.AggregateMappingManager; import software.coley.recaf.services.mapping.aggregate.AggregatedMappings; import software.coley.recaf.services.mapping.gen.MappingGenerator; @@ -37,7 +38,7 @@ */ class MappingApplierTest extends TestBase { static NameGenerator nameGenerator; - static MappingGenerator mappingGenerator; + MappingGenerator mappingGenerator; Workspace workspace; WorkspaceResource resource; AggregateMappingManager aggregateMappingManager; @@ -71,7 +72,7 @@ void prepareWorkspace() throws IOException { resource = workspace.getPrimaryResource(); workspaceManager.setCurrent(workspace); aggregateMappingManager = recaf.get(AggregateMappingManager.class); - inheritanceGraph = recaf.get(InheritanceGraph.class); + inheritanceGraph = recaf.get(InheritanceGraphService.class).getCurrentWorkspaceInheritanceGraph(); mappingGenerator = recaf.get(MappingGenerator.class); mappingApplier = recaf.get(MappingApplier.class); } diff --git a/recaf-core/src/test/java/software/coley/recaf/services/mapping/gen/MappingGeneratorTest.java b/recaf-core/src/test/java/software/coley/recaf/services/mapping/gen/MappingGeneratorTest.java index fa3836929..732e76599 100644 --- a/recaf-core/src/test/java/software/coley/recaf/services/mapping/gen/MappingGeneratorTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/services/mapping/gen/MappingGeneratorTest.java @@ -10,6 +10,7 @@ import software.coley.recaf.info.member.LocalVariable; import software.coley.recaf.info.member.MethodMember; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.mapping.IntermediateMappings; import software.coley.recaf.services.mapping.Mappings; import software.coley.recaf.services.mapping.data.MethodMapping; @@ -85,7 +86,7 @@ public String mapVariable(@Nonnull ClassInfo owner, @Nonnull MethodMember declar )); resource = workspace.getPrimaryResource(); workspaceManager.setCurrent(workspace); - inheritanceGraph = recaf.get(InheritanceGraph.class); + inheritanceGraph = recaf.get(InheritanceGraphService.class).getCurrentWorkspaceInheritanceGraph(); mappingGenerator = recaf.get(MappingGenerator.class); strMatchProvider = recaf.get(StringPredicateProvider.class); } diff --git a/recaf-core/src/test/java/software/coley/recaf/services/transform/TransformationApplierTest.java b/recaf-core/src/test/java/software/coley/recaf/services/transform/TransformationApplierTest.java index 970efe862..99307ada0 100644 --- a/recaf-core/src/test/java/software/coley/recaf/services/transform/TransformationApplierTest.java +++ b/recaf-core/src/test/java/software/coley/recaf/services/transform/TransformationApplierTest.java @@ -4,9 +4,8 @@ import org.junit.jupiter.api.Test; import software.coley.recaf.info.JvmClassInfo; import software.coley.recaf.services.inheritance.InheritanceGraph; -import software.coley.recaf.services.inheritance.InheritanceGraphConfig; -import software.coley.recaf.services.mapping.MappingListeners; -import software.coley.recaf.services.mapping.MappingListenersConfig; +import software.coley.recaf.services.inheritance.InheritanceGraphService; +import software.coley.recaf.test.TestBase; import software.coley.recaf.test.TestClassUtils; import software.coley.recaf.test.dummy.HelloWorld; import software.coley.recaf.workspace.model.Workspace; @@ -27,7 +26,7 @@ /** * Tests for {@link TransformationApplier} */ -class TransformationApplierTest { +class TransformationApplierTest extends TestBase { private static final TransformationApplierConfig config = new TransformationApplierConfig(); private static final InheritanceGraph graph; private static final Workspace workspace; @@ -36,14 +35,10 @@ class TransformationApplierTest { // Make a dummy workspace. We just need a single class (and any class will work) try { workspace = TestClassUtils.fromBundle(TestClassUtils.fromClasses(HelloWorld.class)); + graph = recaf.get(InheritanceGraphService.class).newInheritanceGraph(workspace); } catch (IOException e) { throw new RuntimeException("Failed to read input class for transformer test", e); } - - // Make the graph - MappingListeners listeners = new MappingListeners(new MappingListenersConfig()); - InheritanceGraphConfig graphConfig = new InheritanceGraphConfig(); - graph = new InheritanceGraph(graphConfig, listeners, workspace); } @Test @@ -60,8 +55,8 @@ void independentAB() { // If we transform with "B" we should observe that only "B" is called on sine the two hold no relation TransformationManager manager = new TransformationManager(map); - TransformationApplier applier = new TransformationApplier(manager, graph, config); - assertDoesNotThrow(() -> applier.transformJvm(workspace, Collections.singletonList(JvmTransformerB.class))); + TransformationApplier applier = new TransformationApplier(manager, graph, workspace); + assertDoesNotThrow(() -> applier.transformJvm(Collections.singletonList(JvmTransformerB.class))); // "A" not used verify(transformerA, never()).transform(any(), any(), any(), any(), any()); @@ -84,8 +79,8 @@ void dependentAB() { // If we transform with "B" we should observe that both "B" and "A" were called on. TransformationManager manager = new TransformationManager(map); - TransformationApplier applier = new TransformationApplier(manager, graph, config); - assertDoesNotThrow(() -> applier.transformJvm(workspace, Collections.singletonList(JvmTransformerDependingOnA.class))); + TransformationApplier applier = new TransformationApplier(manager, graph, workspace); + assertDoesNotThrow(() -> applier.transformJvm(Collections.singletonList(JvmTransformerDependingOnA.class))); verify(transformerA, times(1)).transform(any(), same(workspace), any(), any(), any()); verify(transformerB, times(1)).transform(any(), same(workspace), any(), any(), any()); } @@ -104,9 +99,9 @@ void cycleAB() { // If we transform with "A" or "B" we should observe an exception due to the detected cycle TransformationManager manager = new TransformationManager(map); - TransformationApplier applier = new TransformationApplier(manager, graph, config); - assertThrows(TransformationException.class, () -> applier.transformJvm(workspace, Collections.singletonList(JvmCycleA.class))); - assertThrows(TransformationException.class, () -> applier.transformJvm(workspace, Collections.singletonList(JvmCycleB.class))); + TransformationApplier applier = new TransformationApplier(manager, graph, workspace); + assertThrows(TransformationException.class, () -> applier.transformJvm(Collections.singletonList(JvmCycleA.class))); + assertThrows(TransformationException.class, () -> applier.transformJvm(Collections.singletonList(JvmCycleB.class))); verify(transformerA, never()).transform(any(), same(workspace), any(), any(), any()); verify(transformerB, never()).transform(any(), same(workspace), any(), any(), any()); } @@ -122,8 +117,8 @@ void cycleSingle() { // If we transform with the single transformer we should observe an exception due to the detected cycle TransformationManager manager = new TransformationManager(map); - TransformationApplier applier = new TransformationApplier(manager, graph, config); - assertThrows(TransformationException.class, () -> applier.transformJvm(workspace, Collections.singletonList(JvmCycleSingle.class))); + TransformationApplier applier = new TransformationApplier(manager, graph, workspace); + assertThrows(TransformationException.class, () -> applier.transformJvm(Collections.singletonList(JvmCycleSingle.class))); verify(transformer, never()).transform(any(), same(workspace), any(), any(), any()); } @@ -131,8 +126,8 @@ void cycleSingle() { void missingRegistration() { // If we transform with a transformer that is not registered in the manager, the transform should fail TransformationManager manager = new TransformationManager(Collections.emptyMap()); - TransformationApplier applier = new TransformationApplier(manager, graph, config); - assertThrows(TransformationException.class, () -> applier.transformJvm(workspace, Collections.singletonList(JvmCycleSingle.class))); + TransformationApplier applier = new TransformationApplier(manager, graph, workspace); + assertThrows(TransformationException.class, () -> applier.transformJvm(Collections.singletonList(JvmCycleSingle.class))); } static class JvmTransformerA implements JvmClassTransformer { diff --git a/recaf-ui/src/main/java/software/coley/recaf/ui/control/graph/MethodCallGraphsPane.java b/recaf-ui/src/main/java/software/coley/recaf/ui/control/graph/MethodCallGraphsPane.java index 1ec09f4b8..fe09effd5 100644 --- a/recaf-ui/src/main/java/software/coley/recaf/ui/control/graph/MethodCallGraphsPane.java +++ b/recaf-ui/src/main/java/software/coley/recaf/ui/control/graph/MethodCallGraphsPane.java @@ -44,7 +44,7 @@ public MethodCallGraphsPane(@Nonnull Workspace workspace, @Nonnull CallGraphServ @Nonnull CellConfigurationService configurationService) { currentMethodInfo = new SimpleObjectProperty<>(); - CallGraph callGraph = Objects.requireNonNull(callGraphService.getCurrentWorkspaceGraph(), "Graph not created"); + CallGraph callGraph = Objects.requireNonNull(callGraphService.getCurrentWorkspaceCallGraph(), "Graph not created"); getTabs().add(creatTab(workspace, callGraph, configurationService, format, actions, MethodCallGraphPane.CallGraphMode.CALLS, currentMethodInfo)); getTabs().add(creatTab(workspace, callGraph, configurationService, format, actions, MethodCallGraphPane.CallGraphMode.CALLERS, currentMethodInfo)); diff --git a/recaf-ui/src/main/java/software/coley/recaf/ui/pane/MappingGeneratorPane.java b/recaf-ui/src/main/java/software/coley/recaf/ui/pane/MappingGeneratorPane.java index 5c892ed27..08aecc3a0 100644 --- a/recaf-ui/src/main/java/software/coley/recaf/ui/pane/MappingGeneratorPane.java +++ b/recaf-ui/src/main/java/software/coley/recaf/ui/pane/MappingGeneratorPane.java @@ -13,13 +13,32 @@ import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.binding.StringBinding; -import javafx.beans.property.*; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; -import javafx.scene.control.*; -import javafx.scene.layout.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.MenuButton; +import javafx.scene.control.SplitPane; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.scene.text.TextAlignment; import javafx.util.StringConverter; import org.kordamp.ikonli.carbonicons.CarbonIcons; @@ -31,6 +50,7 @@ import software.coley.recaf.services.config.ConfigComponentFactory; import software.coley.recaf.services.config.ConfigComponentManager; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.mapping.IntermediateMappings; import software.coley.recaf.services.mapping.MappingApplier; import software.coley.recaf.services.mapping.MappingResults; @@ -40,21 +60,37 @@ import software.coley.recaf.services.mapping.format.EnigmaMappings; import software.coley.recaf.services.mapping.gen.MappingGenerator; import software.coley.recaf.services.mapping.gen.filter.*; -import software.coley.recaf.services.mapping.gen.naming.*; +import software.coley.recaf.services.mapping.gen.naming.DeconflictingNameGenerator; +import software.coley.recaf.services.mapping.gen.naming.IncrementingNameGeneratorProvider; +import software.coley.recaf.services.mapping.gen.naming.NameGenerator; +import software.coley.recaf.services.mapping.gen.naming.NameGeneratorProvider; +import software.coley.recaf.services.mapping.gen.naming.NameGeneratorProviders; import software.coley.recaf.services.search.match.StringPredicate; import software.coley.recaf.services.search.match.StringPredicateProvider; import software.coley.recaf.ui.LanguageStylesheets; -import software.coley.recaf.ui.control.*; +import software.coley.recaf.ui.control.ActionButton; +import software.coley.recaf.ui.control.ActionMenuItem; +import software.coley.recaf.ui.control.BoundCheckBox; +import software.coley.recaf.ui.control.BoundComboBox; +import software.coley.recaf.ui.control.BoundIntSpinner; +import software.coley.recaf.ui.control.BoundLabel; +import software.coley.recaf.ui.control.BoundTextField; +import software.coley.recaf.ui.control.FontIconView; import software.coley.recaf.ui.control.richtext.Editor; import software.coley.recaf.ui.control.richtext.search.SearchBar; import software.coley.recaf.ui.control.richtext.syntax.RegexLanguages; import software.coley.recaf.ui.control.richtext.syntax.RegexSyntaxHighlighter; -import software.coley.recaf.util.*; +import software.coley.recaf.util.AccessFlag; +import software.coley.recaf.util.FxThreadUtil; +import software.coley.recaf.util.Lang; +import software.coley.recaf.util.StringUtil; +import software.coley.recaf.util.ToStringConverter; import software.coley.recaf.workspace.model.Workspace; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Function; @@ -94,7 +130,7 @@ public MappingGeneratorPane(@Nonnull Workspace workspace, @Nonnull StringPredicateProvider stringPredicateProvider, @Nonnull MappingGenerator mappingGenerator, @Nonnull ConfigComponentManager componentManager, - @Nonnull InheritanceGraph graph, + @Nonnull InheritanceGraphService graphService, @Nonnull AggregateMappingManager aggregateMappingManager, @Nonnull MappingApplier mappingApplier, @Nonnull Instance searchBarProvider) { @@ -104,7 +140,7 @@ public MappingGeneratorPane(@Nonnull Workspace workspace, this.stringPredicateProvider = stringPredicateProvider; this.mappingGenerator = mappingGenerator; this.componentManager = componentManager; - this.graph = graph; + this.graph = Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created"); this.mappingApplier = mappingApplier; // Cache text matchers. diff --git a/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/assembler/AssemblerPane.java b/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/assembler/AssemblerPane.java index 9406c1cee..e9b256b4a 100644 --- a/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/assembler/AssemblerPane.java +++ b/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/assembler/AssemblerPane.java @@ -29,6 +29,7 @@ import software.coley.recaf.services.assembler.AssemblerPipeline; import software.coley.recaf.services.assembler.AssemblerPipelineManager; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.navigation.ClassNavigable; import software.coley.recaf.services.navigation.UpdatableNavigable; import software.coley.recaf.services.workspace.WorkspaceManager; @@ -92,12 +93,13 @@ public AssemblerPane(@Nonnull AssemblerPipelineManager pipelineManager, @Nonnull KeybindingConfig keys, @Nonnull SideTabsInjector sideTabsInjector, @Nonnull WorkspaceManager workspaceManager, - @Nonnull InheritanceGraph graph) { + @Nonnull InheritanceGraphService graphService) { this.pipelineManager = pipelineManager; this.assemblerToolTabs = assemblerToolTabs; int timeToWait = pipelineManager.getServiceConfig().getDisassemblyAstParseDelay().getValue(); + InheritanceGraph graph = Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created"); tabCompleter = new AssemblerTabCompleter(Objects.requireNonNull(workspaceManager.getCurrent()), graph); editor.setTabCompleter(tabCompleter); editor.getCodeArea().getStylesheets().add(LanguageStylesheets.getJasmStylesheet()); diff --git a/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/tabs/InheritancePane.java b/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/tabs/InheritancePane.java index bd77354b3..0be07492e 100644 --- a/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/tabs/InheritancePane.java +++ b/recaf-ui/src/main/java/software/coley/recaf/ui/pane/editing/tabs/InheritancePane.java @@ -19,6 +19,7 @@ import software.coley.recaf.services.cell.CellConfigurationService; import software.coley.recaf.services.cell.context.ContextSource; import software.coley.recaf.services.inheritance.InheritanceGraph; +import software.coley.recaf.services.inheritance.InheritanceGraphService; import software.coley.recaf.services.inheritance.InheritanceVertex; import software.coley.recaf.services.navigation.Navigable; import software.coley.recaf.services.navigation.UpdatableNavigable; @@ -47,9 +48,9 @@ public class InheritancePane extends StackPane implements UpdatableNavigable { private ClassPathNode path; @Inject - public InheritancePane(@Nonnull InheritanceGraph inheritanceGraph, + public InheritancePane(@Nonnull InheritanceGraphService graphService, @Nonnull CellConfigurationService configurationService) { - this.inheritanceGraph = inheritanceGraph; + this.inheritanceGraph = Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created"); contentType.addListener((ob, old, cur) -> regenerateTree()); // Configure tree.