Skip to content

Commit

Permalink
Refactor MappingApplier to not be workspace-scoped, and usable for an…
Browse files Browse the repository at this point in the history
…y arbitrary workspace
  • Loading branch information
Col-E committed Dec 8, 2024
1 parent 5484258 commit f6651e4
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 74 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package software.coley.recaf.services.mapping;

import jakarta.annotation.Nonnull;
import jakarta.inject.Inject;
import jakarta.annotation.Nullable;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import software.coley.recaf.cdi.WorkspaceScoped;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.info.properties.builtin.HasMappedReferenceProperty;
import software.coley.recaf.info.properties.builtin.OriginalClassNameProperty;
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.services.workspace.WorkspaceManager;
import software.coley.recaf.util.threading.ThreadPoolFactory;
import software.coley.recaf.util.threading.ThreadUtil;
import software.coley.recaf.util.visitors.IllegalSignatureRemovingVisitor;
Expand All @@ -22,7 +20,6 @@
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;

Expand All @@ -33,27 +30,33 @@
* @author Matt Coley
* @see MappingResults
*/
@WorkspaceScoped
public class MappingApplier implements Service {
public static final String SERVICE_ID = "mapping-applier";
private static final ExecutorService applierThreadPool = ThreadPoolFactory.newFixedThreadPool(SERVICE_ID);
public class MappingApplier {
private static final ExecutorService applierThreadPool = ThreadPoolFactory.newFixedThreadPool(MappingApplierService.SERVICE_ID);
private final InheritanceGraph inheritanceGraph;
private final AggregateMappingManager aggregateMappingManager;
private final MappingListeners listeners;
private final Workspace workspace;
private final MappingApplierConfig config;

@Inject
public MappingApplier(@Nonnull MappingApplierConfig config,
@Nonnull InheritanceGraphService graphService,
@Nonnull AggregateMappingManager aggregateMappingManager,
@Nonnull MappingListeners listeners,
@Nonnull Workspace workspace) {
this.inheritanceGraph = Objects.requireNonNull(graphService.getCurrentWorkspaceInheritanceGraph(), "Graph not created");

/**
* @param workspace
* Workspace to apply mappings in.
* @param inheritanceGraph
* Inheritance graph for the given workspace.
* @param listeners
* Application mapping listeners
* <i>(If the target workspace is the {@link WorkspaceManager#getCurrent() current one})</i>
* @param aggregateMappingManager
* Aggregate mappings for tracking applications in the current workspace
* <i>(If the target workspace is the {@link WorkspaceManager#getCurrent() current one})</i>
*/
public MappingApplier(@Nonnull Workspace workspace,
@Nonnull InheritanceGraph inheritanceGraph,
@Nullable MappingListeners listeners,
@Nullable AggregateMappingManager aggregateMappingManager) {
this.inheritanceGraph = inheritanceGraph;
this.aggregateMappingManager = aggregateMappingManager;
this.listeners = listeners;
this.workspace = workspace;
this.config = config;
}

/**
Expand All @@ -76,8 +79,10 @@ public MappingResults applyToClasses(@Nonnull Mappings mappings,
@Nonnull JvmClassBundle bundle,
@Nonnull Collection<JvmClassInfo> classes) {
mappings = enrich(mappings);
MappingResults results = new MappingResults(mappings, listeners.createBundledMappingApplicationListener())
.withAggregateManager(aggregateMappingManager);
MappingApplicationListener listener = listeners == null ? null : listeners.createBundledMappingApplicationListener();
MappingResults results = new MappingResults(mappings, listener);
if (aggregateMappingManager != null)
results.withAggregateManager(aggregateMappingManager);

// Apply mappings to the provided classes, collecting into the results model.
Mappings finalMappings = mappings;
Expand All @@ -101,14 +106,15 @@ public MappingResults applyToClasses(@Nonnull Mappings mappings,
@Nonnull
public MappingResults applyToPrimaryResource(@Nonnull Mappings mappings) {
mappings = enrich(mappings);
WorkspaceResource resource = workspace.getPrimaryResource();

MappingResults results = new MappingResults(mappings, listeners.createBundledMappingApplicationListener())
.withAggregateManager(aggregateMappingManager);
MappingApplicationListener listener = listeners == null ? null : listeners.createBundledMappingApplicationListener();
MappingResults results = new MappingResults(mappings, listener);
if (aggregateMappingManager != null)
results.withAggregateManager(aggregateMappingManager);

// Apply mappings to all classes in the primary resource, collecting into the results model.
Mappings finalMappings = mappings;
ExecutorService service = ThreadUtil.phasingService(applierThreadPool);
WorkspaceResource resource = workspace.getPrimaryResource();
Stream.concat(resource.jvmClassBundleStream(), resource.versionedJvmClassBundleStream()).forEach(bundle -> {
bundle.forEach(classInfo -> {
service.execute(() -> dumpIntoResults(results, workspace, resource, bundle, classInfo, finalMappings));
Expand Down Expand Up @@ -199,16 +205,4 @@ private static void dumpIntoResults(@Nonnull MappingResults results,
results.add(workspace, resource, bundle, classInfo, updatedInfo);
}
}

@Nonnull
@Override
public String getServiceId() {
return SERVICE_ID;
}

@Nonnull
@Override
public MappingApplierConfig getServiceConfig() {
return config;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import software.coley.recaf.services.ServiceConfig;

/**
* Config for {@link MappingApplier}
* Config for {@link MappingApplierService}
*
* @author Matt Coley
*/
@ApplicationScoped
public class MappingApplierConfig extends BasicConfigContainer implements ServiceConfig {
@Inject
public MappingApplierConfig() {
super(ConfigGroups.SERVICE_MAPPING, MappingApplier.SERVICE_ID + CONFIG_SUFFIX);
super(ConfigGroups.SERVICE_MAPPING, MappingApplierService.SERVICE_ID + CONFIG_SUFFIX);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package software.coley.recaf.services.mapping;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
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.services.workspace.WorkspaceManager;
import software.coley.recaf.util.threading.ThreadPoolFactory;
import software.coley.recaf.workspace.model.Workspace;

import java.util.Objects;
import java.util.concurrent.ExecutorService;

/**
* Service offering the creation of {@link MappingApplier mapping appliers} for workspaces.
*
* @author Matt Coley
* @see MappingApplier
*/
@ApplicationScoped
public class MappingApplierService implements Service {
public static final String SERVICE_ID = "mapping-applier";
private static final ExecutorService applierThreadPool = ThreadPoolFactory.newFixedThreadPool(SERVICE_ID);
private final InheritanceGraphService inheritanceGraphService;
private final AggregateMappingManager aggregateMappingManager;
private final MappingListeners listeners;
private final WorkspaceManager workspaceManager;
private final MappingApplierConfig config;

@Inject
public MappingApplierService(@Nonnull MappingApplierConfig config,
@Nonnull InheritanceGraphService inheritanceGraphService,
@Nonnull AggregateMappingManager aggregateMappingManager,
@Nonnull MappingListeners listeners,
@Nonnull WorkspaceManager workspaceManager) {
this.inheritanceGraphService = inheritanceGraphService;
this.aggregateMappingManager = aggregateMappingManager;
this.listeners = listeners;
this.workspaceManager = workspaceManager;
this.config = config;
}

/**
* @param workspace
* Workspace to apply mappings in.
*
* @return Applier for the given workspace.
*/
@Nonnull
public MappingApplier inWorkspace(@Nonnull Workspace workspace) {
if (workspace == workspaceManager.getCurrent())
return Objects.requireNonNull(inCurrentWorkspace(), "Failed to access current workspace for mapping application");
return new MappingApplier(workspace, inheritanceGraphService.newInheritanceGraph(workspace), null, null);
}

/**
* @return Applier for the current workspace, or {@code null} if no workspace is open.
*/
@Nullable
public MappingApplier inCurrentWorkspace() {
Workspace workspace = workspaceManager.getCurrent();
if (workspace == null)
return null;
InheritanceGraph currentWorkspaceInheritanceGraph = inheritanceGraphService.getCurrentWorkspaceInheritanceGraph();
if (currentWorkspaceInheritanceGraph == null)
return null;
return new MappingApplier(workspace, currentWorkspaceInheritanceGraph, listeners, aggregateMappingManager);
}

@Nonnull
@Override
public String getServiceId() {
return SERVICE_ID;
}

@Nonnull
@Override
public MappingApplierConfig getServiceConfig() {
return config;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package software.coley.recaf.services.comment;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import software.coley.recaf.info.JvmClassInfo;
import software.coley.recaf.path.ClassPathNode;
import software.coley.recaf.services.decompile.DecompileResult;
import software.coley.recaf.services.decompile.DecompilerManager;
import software.coley.recaf.services.mapping.IntermediateMappings;
import software.coley.recaf.services.mapping.MappingApplier;
import software.coley.recaf.services.mapping.MappingApplierService;
import software.coley.recaf.services.mapping.MappingResults;
import software.coley.recaf.services.workspace.WorkspaceManager;
import software.coley.recaf.test.TestBase;
Expand All @@ -26,7 +30,7 @@ class CommentManagerTest extends TestBase {
static CommentManager commentManager;
static CommentManagerConfig commentManagerConfig;
static DecompilerManager decompilerManager;
static MappingApplier mappingApplier;
static MappingApplierService mappingApplierService;
static JvmClassInfo classToDecompile;
static Workspace workspace;

Expand All @@ -41,7 +45,7 @@ static void setup() throws IOException {
commentManager = recaf.get(CommentManager.class);
commentManagerConfig = recaf.get(CommentManagerConfig.class);
decompilerManager = recaf.get(DecompilerManager.class);
mappingApplier = recaf.get(MappingApplier.class);
mappingApplierService = recaf.get(MappingApplierService.class);
}

@Test
Expand Down Expand Up @@ -97,7 +101,7 @@ void testCommentsGetMigratedAfterRemapping() {
mappings.addMethod(classToDecompile.getName(), "()V", "methodWithLocalVariables", "fizz");

// Apply the mappings
MappingResults results = mappingApplier.applyToPrimaryResource(mappings);
MappingResults results = mappingApplierService.inCurrentWorkspace().applyToPrimaryResource(mappings);
ClassPathNode postMappingPath = results.getPostMappingPath(classToDecompile.getName());
assertNotNull(postMappingPath, "Post-mapping path does not exist in mapping results");
results.apply();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import software.coley.recaf.info.builder.JvmClassInfoBuilder;
import software.coley.recaf.services.mapping.IntermediateMappings;
import software.coley.recaf.services.mapping.MappingApplier;
import software.coley.recaf.services.mapping.MappingApplierService;
import software.coley.recaf.services.mapping.MappingResults;
import software.coley.recaf.test.TestBase;
import software.coley.recaf.test.TestClassUtils;
Expand All @@ -26,7 +27,7 @@
class InheritanceAndRenamingTest extends TestBase {
static Workspace workspace;
static InheritanceGraph inheritanceGraph;
static MappingApplier mappingApplier;
static MappingApplierService mappingApplierService;
static JvmClassInfo[] generatedClasses;

@BeforeAll
Expand All @@ -48,7 +49,7 @@ static void setup() {
inheritanceGraph.toString(); // Force immediate init.

// Get mapping applier
mappingApplier = recaf.get(MappingApplier.class);
mappingApplierService = recaf.get(MappingApplierService.class);
}

@Test
Expand All @@ -64,7 +65,7 @@ void test() {
IntermediateMappings mappings = new IntermediateMappings();
for (int i = 1; i <= 5; i++)
mappings.addClass("I" + i, "R" + i);
MappingResults results = mappingApplier.applyToPrimaryResource(mappings);
MappingResults results = mappingApplierService.inCurrentWorkspace().applyToPrimaryResource(mappings);
results.apply();

// Very old classes are removed from the graph
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class MappingApplierTest extends TestBase {
WorkspaceResource resource;
AggregateMappingManager aggregateMappingManager;
InheritanceGraph inheritanceGraph;
MappingApplier mappingApplier;
MappingApplierService mappingApplierService;

@BeforeAll
static void setupGenerator() {
Expand Down Expand Up @@ -74,7 +74,7 @@ void prepareWorkspace() throws IOException {
aggregateMappingManager = recaf.get(AggregateMappingManager.class);
inheritanceGraph = recaf.get(InheritanceGraphService.class).getCurrentWorkspaceInheritanceGraph();
mappingGenerator = recaf.get(MappingGenerator.class);
mappingApplier = recaf.get(MappingApplier.class);
mappingApplierService = recaf.get(MappingApplierService.class);
}

@Test
Expand Down Expand Up @@ -105,7 +105,7 @@ public boolean shouldMapMethod(@Nonnull ClassInfo owner, @Nonnull MethodMember m
});

// Preview the mapping operation
MappingResults results = mappingApplier.applyToPrimaryResource(mappings);
MappingResults results = mappingApplierService.inCurrentWorkspace().applyToPrimaryResource(mappings);

// The supplier class we define should be remapped.
// The runner class (AnonymousLambda) itself should not be remapped, but should be updated to point to
Expand Down Expand Up @@ -160,7 +160,7 @@ public boolean shouldMapMethod(@Nonnull ClassInfo owner, @Nonnull MethodMember m
});

// Preview the mapping operation
MappingResults results = mappingApplier.applyToPrimaryResource(mappings);
MappingResults results = mappingApplierService.inCurrentWorkspace().applyToPrimaryResource(mappings);

// The enum class we define should be remapped.
// The runner class (DummyEnumPrinter) itself should not be remapped, but should be updated to point to
Expand Down Expand Up @@ -199,7 +199,7 @@ public boolean shouldMapClass(@Nonnull ClassInfo info) {
});

// Preview the mapping operation
MappingResults results = mappingApplier.applyToPrimaryResource(mappings);
MappingResults results = mappingApplierService.inCurrentWorkspace().applyToPrimaryResource(mappings);

// The annotation class we define should be remapped.
// The user class (ClassWithAnnotation) itself should not be remapped,
Expand Down Expand Up @@ -254,7 +254,7 @@ public boolean shouldMapMethod(@Nonnull ClassInfo owner, @Nonnull MethodMember m
});

// Preview the mapping operation
MappingResults results = mappingApplier.applyToPrimaryResource(mappings);
MappingResults results = mappingApplierService.inCurrentWorkspace().applyToPrimaryResource(mappings);

assertNotNull(mappings.getMappedClassName(overlapInterfaceAName), "OverlapInterfaceA should be remapped");
assertNotNull(mappings.getMappedClassName(overlapInterfaceBName), "OverlapInterfaceB should be remapped");
Expand Down
Loading

0 comments on commit f6651e4

Please sign in to comment.