Skip to content

Commit

Permalink
Support additional tiny v1 layouts, tiny v2 via mapping-io
Browse files Browse the repository at this point in the history
  • Loading branch information
Col-E committed Feb 25, 2024
1 parent 0fa3551 commit 14a4475
Show file tree
Hide file tree
Showing 14 changed files with 274 additions and 145 deletions.
2 changes: 2 additions & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ project.ext {

logging_impl = 'ch.qos.logback:logback-classic:1.4.11' // newer releases break in jar releases

mapping_io = 'net.fabricmc:mapping-io:0.5.1'

mockito = 'org.mockito:mockito-core:5.6.0'

openrewrite = 'org.openrewrite:rewrite-java-17:8.13.3'
Expand Down
1 change: 1 addition & 0 deletions recaf-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {
api jphantom
api llzip
api logging_impl
api mapping_io
api picocli
api procyon
api jackson
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public EnigmaMappings() {
super(NAME, true, true);
}

@Nonnull
@Override
public IntermediateMappings parse(@Nonnull String mappingText) {
IntermediateMappings mappings = new IntermediateMappings();
Expand Down Expand Up @@ -112,7 +113,7 @@ public IntermediateMappings parse(@Nonnull String mappingText) {
}

@Override
public String exportText(Mappings mappings) {
public String exportText(@Nonnull Mappings mappings) {
StringBuilder sb = new StringBuilder();
IntermediateMappings intermediate = mappings.exportIntermediate();
for (String oldClassName : intermediate.getClassesWithMappings()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package software.coley.recaf.services.mapping.format;

import jakarta.annotation.Nonnull;

/**
* Wrapper to encompass any error encountered during mapping format reading / writing.
*
* @author Matt Coley
*/
public class InvalidMappingException extends Exception {
/**
* @param cause
* Cause for mapping parse/write failure.
*/
public InvalidMappingException(@Nonnull Throwable cause) {
super(cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public JadxMappings() {
super(NAME, true, true);
}

@Nonnull
@Override
public IntermediateMappings parse(@Nonnull String mappingText) {
IntermediateMappings mappings = new IntermediateMappings();
Expand Down Expand Up @@ -88,7 +89,7 @@ public IntermediateMappings parse(@Nonnull String mappingText) {
}

@Override
public String exportText(Mappings mappings) {
public String exportText(@Nonnull Mappings mappings) {
StringBuilder sb = new StringBuilder();
IntermediateMappings intermediate = mappings.exportIntermediate();
for (String oldClassName : intermediate.getClassesWithMappings()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
package software.coley.recaf.services.mapping.format;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import net.fabricmc.mappingio.MappedElementKind;
import net.fabricmc.mappingio.MappingVisitor;
import net.fabricmc.mappingio.tree.MappingTree;
import net.fabricmc.mappingio.tree.MemoryMappingTree;
import net.fabricmc.mappingio.tree.VisitOrder;
import software.coley.recaf.services.mapping.IntermediateMappings;
import software.coley.recaf.services.mapping.Mappings;
import software.coley.recaf.services.mapping.data.ClassMapping;
import software.coley.recaf.services.mapping.data.FieldMapping;
import software.coley.recaf.services.mapping.data.MethodMapping;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.List;
import java.util.function.Function;

/**
* Interface to use for explicit file format implementations of {@link Mappings}.
Expand Down Expand Up @@ -31,8 +46,12 @@ public interface MappingFileFormat {
* Text of the mappings to parse.
*
* @return Intermediate mappings from parsed text.
*
* @throws InvalidMappingException
* When reading the mappings encounters any failure.
*/
IntermediateMappings parse(@Nonnull String mappingsText);
@Nonnull
IntermediateMappings parse(@Nonnull String mappingsText) throws InvalidMappingException;

/**
* Some mapping formats do not include field types since name overloading is illegal at the source level of Java.
Expand Down Expand Up @@ -65,9 +84,107 @@ default boolean supportsExportText() {
* @param mappings
* Mappings to write with the current format.
*
* @return Exported mapping text in the current format.
* @return Exported mapping text in the current format. {@code null} if exporting to the format is unsupported.
*
* @throws InvalidMappingException
* When writing the mappings encounters any failure.
*/
default String exportText(Mappings mappings) {
@Nullable
default String exportText(@Nonnull Mappings mappings) throws InvalidMappingException {
return null;
}

/**
* A utility for utilizing mapping-io to parse mapping text formats.
*
* @param mappingText
* Text of mapping to parse.
* @param visitor
* Visitor pointing to a mapping-io format reader.
*
* @return Intermediate mapping representation of the parsed text.
*
* @throws InvalidMappingException
* When reading the mappings encounters any failure.
*/
@Nonnull
static IntermediateMappings parse(@Nonnull String mappingText, @Nonnull MappingTreeReader visitor) throws InvalidMappingException {
IntermediateMappings mappings = new IntermediateMappings();
MemoryMappingTree tree = new MemoryMappingTree();
StringReader reader = new StringReader(mappingText);
try {
visitor.read(reader, tree);
} catch (IOException ex) {
throw new InvalidMappingException(ex);
}
int namespaceCount = tree.getDstNamespaces().size();
int finalNamespace = namespaceCount - 1;
for (MappingTree.ClassMapping cm : tree.getClasses()) {
String finalClassName = cm.getDstName(finalNamespace);
mappings.addClass(cm.getSrcName(), finalClassName);
if (namespaceCount > 1)
for (int i = 0; i < finalNamespace; i++)
mappings.addClass(cm.getDstName(i), finalClassName);
for (MappingTree.FieldMapping fm : cm.getFields()) {
String finalFieldName = fm.getDstName(finalNamespace);
mappings.addField(cm.getSrcName(), fm.getSrcDesc(), fm.getSrcName(), finalFieldName);
if (namespaceCount > 1)
for (int i = 0; i < finalNamespace; i++)
mappings.addField(cm.getSrcName(), fm.getSrcDesc(), fm.getDstName(i), finalFieldName);
}
for (MappingTree.MethodMapping mm : cm.getMethods()) {
String finalMethodName = mm.getDstName(finalNamespace);
mappings.addMethod(cm.getSrcName(), mm.getSrcDesc(), mm.getSrcName(), finalMethodName);
if (namespaceCount > 1)
for (int i = 0; i < finalNamespace; i++)
mappings.addMethod(cm.getSrcName(), mm.getSrcDesc(), mm.getDstName(i), finalMethodName);
}
}
return mappings;
}

/**
* A utility for utilizing mapping-io to write mapping text formats.
*
* @param mappings
* Mappings to export to text.
* @param writerFactory
* Factory to create a mapping-io format writer.
*
* @return Text representation of mappings in the format provided by the writer factory.
*
* @throws InvalidMappingException
* When writing the mappings encounters any failure.
*/
@Nonnull
static String export(@Nonnull Mappings mappings, @Nonnull Function<StringWriter, MappingVisitor> writerFactory) throws InvalidMappingException {
MemoryMappingTree tree = new MemoryMappingTree();
IntermediateMappings intermediate = mappings.exportIntermediate();
try {
for (ClassMapping classMapping : intermediate.getClasses().values()) {
String classOriginalName = classMapping.getOldName();
tree.visitClass(classOriginalName);
tree.visitDstName(MappedElementKind.CLASS, 0, classMapping.getNewName());

List<FieldMapping> fieldMappings = intermediate.getClassFieldMappings(classOriginalName);
for (FieldMapping fieldMapping : fieldMappings) {
tree.visitField(fieldMapping.getOldName(), fieldMapping.getDesc());
tree.visitDstName(MappedElementKind.FIELD, 0, fieldMapping.getNewName());
}

List<MethodMapping> methodMappings = intermediate.getClassMethodMappings(classOriginalName);
for (MethodMapping methodMapping : methodMappings) {
tree.visitField(methodMapping.getOldName(), methodMapping.getDesc());
tree.visitDstName(MappedElementKind.METHOD, 0, methodMapping.getNewName());
}
}

StringWriter sw = new StringWriter();
MappingVisitor writer = writerFactory.apply(sw);
tree.accept(writer, VisitOrder.createByInputOrder());
return sw.toString();
} catch (Throwable t) {
throw new InvalidMappingException(t);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package software.coley.recaf.services.mapping.format;

import jakarta.annotation.Nonnull;
import net.fabricmc.mappingio.MappingVisitor;
import net.fabricmc.mappingio.format.tiny.Tiny1FileReader;

import java.io.IOException;
import java.io.Reader;

/**
* Outlines the read process from a given reader into the given visitor.
* Should point to a file-reader method from mapping-io.
* For instance: {@link Tiny1FileReader#read(Reader, MappingVisitor)}.
*
* @author Matt Coley
* @see MappingFileFormat#parse(String, MappingTreeReader)
*/
public interface MappingTreeReader {
/**
* @param reader
* Reader containing the mapping file text.
* @param visitor
* Mapping output visitor.
*
* @throws IOException
* When any mapping parse errors occur.
*/
void read(@Nonnull Reader reader, @Nonnull MappingVisitor visitor) throws IOException;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package software.coley.recaf.services.mapping.format;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import jakarta.enterprise.context.Dependent;
import net.fabricmc.mappingio.format.proguard.ProGuardFileWriter;
import net.fabricmc.mappingio.format.tiny.Tiny2FileWriter;
import software.coley.recaf.services.mapping.IntermediateMappings;
import software.coley.recaf.services.mapping.Mappings;
import software.coley.recaf.util.StringUtil;

import java.util.Arrays;
Expand All @@ -27,6 +31,7 @@ public ProguardMappings() {
super(NAME, true, false);
}

@Nonnull
@Override
public IntermediateMappings parse(@Nonnull String mappingsText) {
IntermediateMappings mappings = new IntermediateMappings();
Expand Down Expand Up @@ -169,11 +174,10 @@ private static String denormalizeType(String type, StringBuilder stringCache, Ma
return type;
}

@Nullable
@Override
public boolean supportsExportText() {
// You see how cringe the file parsing is?
// Surely you understand why this is 'false'...
return false;
public String exportText(@Nonnull Mappings mappings) throws InvalidMappingException {
return MappingFileFormat.export(mappings, ProGuardFileWriter::new);
}

private static final class ProguardClassInfo {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public SimpleMappings() {
super(NAME, true, true);
}

@Nonnull
@Override
public IntermediateMappings parse(@Nonnull String mappingText) {
IntermediateMappings mappings = new IntermediateMappings();
Expand Down Expand Up @@ -89,7 +90,7 @@ public IntermediateMappings parse(@Nonnull String mappingText) {
}

@Override
public String exportText(Mappings mappings) {
public String exportText(@Nonnull Mappings mappings) {
StringBuilder sb = new StringBuilder();
IntermediateMappings intermediate = mappings.exportIntermediate();
for (String oldClassName : intermediate.getClassesWithMappings()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public SrgMappings() {
super(NAME, false, false);
}

@Nonnull
@Override
public IntermediateMappings parse(@Nonnull String mappingText) {
List<Pair<String, String>> packages = new ArrayList<>();
Expand Down Expand Up @@ -88,7 +89,7 @@ public IntermediateMappings parse(@Nonnull String mappingText) {
}

@Override
public String exportText(Mappings mappings) {
public String exportText(@Nonnull Mappings mappings) {
StringBuilder sb = new StringBuilder();
Remapper remapper = new BasicMappingsRemapper(mappings);
IntermediateMappings intermediate = mappings.exportIntermediate();
Expand Down
Loading

0 comments on commit 14a4475

Please sign in to comment.