MappingGenerator

The mapping generator allows you to generate Mappings for a Workspace based on configurable inputs:

  • Filter what classes, fields, and methods should be included in the generated output mappings via a chain of NameGeneratorFilter items
  • Control the naming scheme of classes, fields, and methods via an implementation of NameGenerator

Filtering what to generate mappings for

What we generate mappings for is controlled by a linked-list of NameGeneratorFilter items. Each item in the chain can generalized to "include this" or "exclude this". Here is an example:

@Inject
StringPredicateProvider strMatchProvider;

// [Access blacklist 'public, protected'] --> [Class whitelist 'com/example/']
//  Any non-public/protected class/field/method in 'com/example' will have a name generated
IncludeClassesFilter includeClasses = new IncludeClassesFilter(null /* tail of linked list */, strMatchProvider.newStartsWithPredicate("com/example/"));
ExcludeModifiersNameFilter excludePublicProtected = new ExcludeModifiersNameFilter(includeClasses, Arrays.asList(Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED), true, true, true);

// Use 'excludePublicProtected' as the 'NameGeneratorFilter' to pass as your filter - It is the head of the linked list.

You can use any of the existing NameGeneratorFilter implementations in the software.coley.recaf.services.mapping.gen.filter package, or make your own.

Controlling the naming scheme

There are a few simple implementations of NameGenerator which can be used as-is, but for more advanced control you'll probably want to make your own. The interface outlines one method for naming each kind of item. Here is a simple implementation:

NameGenerator nameGenerator = new NameGenerator() {
    @Nonnull
    @Override
    public String mapClass(@Nonnull ClassInfo info) {
        return "mapped/Class" + Math.abs(info.getName().hashCode());
    }
    
    @Nonnull
    @Override
    public String mapField(@Nonnull ClassInfo owner, @Nonnull FieldMember field) {
        return "mappedField" + Math.abs(owner.getName().hashCode() + info.getName().hashCode());
    }
    
    @Nonnull
    @Override
    public String mapMethod(@Nonnull ClassInfo owner, @Nonnull MethodMember method) {
        return "mappedMethod" + Math.abs(owner.getName().hashCode() + info.getName().hashCode());
    }
    
    @Nonnull
    @Override
    public String mapVariable(@Nonnull ClassInfo owner, @Nonnull MethodMember declaringMethod, @Nonnull LocalVariable variable) {
        return "mappedVar" + variable.getIndex();
    }
};

Generating the output Mappings

Once you have a NameGenerator and NameGeneratorFilter pass them along to generate(Workspace, WorkspaceResource, InheritanceGraph, NameGenerator, NameGeneratorFilter). The method takes in the Workspace and the WorkspaceResource containing classes you want to generate mappings for. The WorkspaceResouerce will almost always be the workspace's primary resource.

@Inject
InheritanceGraph inheritanceGraph; // You need the inheritance graph associated with the workspace.

Mappings mappings = mappingGenerator.generate(workspace, resource, inheritanceGraph, nameGenerator, filter);