aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorJonas Herzig <me@johni0702.de>2019-04-24 14:52:29 +0200
committerJonas Herzig <me@johni0702.de>2019-04-24 14:52:41 +0200
commit5fba27c5313046ab5c9ccdefeb94a81b292ef8d4 (patch)
treeb546608a939a91f0caf8d5cd30b6caf1ebc54715 /src/main
downloadRemap-5fba27c5313046ab5c9ccdefeb94a81b292ef8d4.tar.gz
Remap-5fba27c5313046ab5c9ccdefeb94a81b292ef8d4.tar.bz2
Remap-5fba27c5313046ab5c9ccdefeb94a81b292ef8d4.zip
Initial commit
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/replaymod/gradle/remap/Transformer.java254
1 files changed, 254 insertions, 0 deletions
diff --git a/src/main/java/com/replaymod/gradle/remap/Transformer.java b/src/main/java/com/replaymod/gradle/remap/Transformer.java
new file mode 100644
index 0000000..c151b7d
--- /dev/null
+++ b/src/main/java/com/replaymod/gradle/remap/Transformer.java
@@ -0,0 +1,254 @@
+package com.replaymod.gradle.remap;
+
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.*;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.text.edits.TextEdit;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+class Transformer {
+ private Map<String, Mapping> map;
+ private String[] classpath;
+
+ public static void main(String[] args) throws IOException, BadLocationException {
+ Map<String, Mapping> mappings;
+ if (args[0].isEmpty()) {
+ mappings = new HashMap<>();
+ } else {
+ mappings = readMappings(Collections.singletonList(new File(args[0])), args[1].equals("true"));
+ }
+ Transformer transformer = new Transformer(mappings);
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+
+ String[] classpath = new String[Integer.parseInt(args[2])];
+ for (int i = 0; i < classpath.length; i++) {
+ classpath[i] = reader.readLine();
+ }
+ transformer.setClasspath(classpath);
+
+ while (true) {
+ String name = reader.readLine();
+ if (name == null || name.isEmpty()) {
+ break;
+ }
+
+ String[] lines = new String[Integer.parseInt(reader.readLine())];
+ for (int i = 0; i < lines.length; i++) {
+ lines[i] = reader.readLine();
+ }
+ String source = String.join("\n", lines);
+
+ String result = transformer.remap(name, source);
+
+ System.out.println(name);
+ lines = result.split("\n");
+ System.out.println(lines.length);
+ for (String line : lines) {
+ System.out.println(line);
+ }
+ }
+ }
+
+ public Transformer(Map<String, Mapping> mappings) {
+ this.map = mappings;
+ }
+
+ public String[] getClasspath() {
+ return classpath;
+ }
+
+ public void setClasspath(String[] classpath) {
+ this.classpath = classpath;
+ }
+
+ public String remap(String unitName, String source) throws BadLocationException {
+ Document document = new Document(source);
+ ASTParser parser = ASTParser.newParser(AST.JLS8);
+ Map<String, String> options = JavaCore.getDefaultOptions();
+ JavaCore.setComplianceOptions("1.8", options);
+ parser.setCompilerOptions(options);
+ parser.setUnitName(unitName);
+ parser.setSource(document.get().toCharArray());
+ parser.setEnvironment(classpath, null, null, true);
+ parser.setResolveBindings(true);
+ parser.setBindingsRecovery(true);
+ CompilationUnit cu = (CompilationUnit) parser.createAST(null);
+ for (IProblem problem : cu.getProblems()) {
+ if (problem.isError()) {
+ System.err.println(unitName + ":" + problem.getSourceLineNumber() + ": " + problem.getMessage());
+ }
+ }
+ cu.recordModifications();
+ if (remapClass(cu)) {
+ TextEdit edit = cu.rewrite(document, JavaCore.getDefaultOptions());
+ edit.apply(document);
+ return document.get();
+ } else {
+ return source;
+ }
+ }
+
+ private boolean remapClass(CompilationUnit cu) {
+ AtomicBoolean changed = new AtomicBoolean(false);
+ Map<String, String> mappedImports = new HashMap<>();
+ cu.accept(new ASTVisitor() {
+ @Override
+ public boolean visit(ImportDeclaration node) {
+ String name = node.getName().getFullyQualifiedName();
+ Mapping mapping = map.get(name);
+ String mapped = mapping == null ? null : mapping.newName;
+ if (mapped != null && !mapped.equals(name)) {
+ node.setName(node.getAST().newName(mapped));
+ changed.set(true);
+ String simpleName = name.substring(name.lastIndexOf('.') + 1);
+ String simpleMapped = mapped.substring(mapped.lastIndexOf('.') + 1);
+ if (!simpleName.equals(simpleMapped)) {
+ mappedImports.put(simpleName, simpleMapped);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(QualifiedName node) {
+ String name = node.getFullyQualifiedName();
+ Mapping mapping = map.get(name);
+ String mapped = mapping == null ? null : mapping.newName;
+ if (mapped != null && !mapped.equals(name)) {
+ node.setQualifier(node.getAST().newName(mapped.substring(0, mapped.lastIndexOf('.'))));
+ node.setName(node.getAST().newSimpleName(mapped.substring(mapped.lastIndexOf('.') + 1)));
+ changed.set(true);
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public boolean visit(SimpleName node) {
+ return visitName(node.resolveBinding(), node);
+ }
+
+ @Override
+ public boolean visit(MethodInvocation node) {
+ return visitName(node.resolveMethodBinding(), node.getName());
+ }
+
+ private boolean visitName(IBinding binding, SimpleName node) {
+ String mapped;
+ if (binding instanceof IVariableBinding) {
+ ITypeBinding declaringClass = ((IVariableBinding) binding).getDeclaringClass();
+ if (declaringClass == null) return true;
+ String name = declaringClass.getQualifiedName();
+ if (name.isEmpty()) return true;
+ Mapping mapping = map.get(name);
+ if (mapping == null) return true;
+ mapped = mapping.fields.get(node.getIdentifier());
+ } else if (binding instanceof IMethodBinding) {
+ ITypeBinding declaringClass = ((IMethodBinding) binding).getDeclaringClass();
+ if (declaringClass == null) return true;
+ String name = declaringClass.getQualifiedName();
+ if (name.isEmpty()) return true;
+ Mapping mapping = map.get(name);
+ if (mapping == null) return true;
+ mapped = mapping.methods.get(node.getIdentifier());
+ } else if (binding instanceof ITypeBinding) {
+ String name = ((ITypeBinding) binding).getQualifiedName();
+ if (name.isEmpty()) return true;
+ Mapping mapping = map.get(name);
+ if (mapping == null) return true;
+ mapped = mapping.newName;
+ mapped = mapped.substring(mapped.lastIndexOf('.') + 1);
+ } else {
+ mapped = mappedImports.get(node.getIdentifier());
+ }
+
+ if (mapped != null && !mapped.equals(node.getIdentifier())) {
+ node.setIdentifier(mapped);
+ changed.set(true);
+ }
+ return true;
+ }
+ });
+ return changed.get();
+ }
+
+ public static class Mapping {
+ public String oldName;
+ public String newName;
+ public Map<String, String> fields = new HashMap<>();
+ public Map<String, String> methods = new HashMap<>();
+ }
+
+ public static Map<String, Mapping> readMappings(List<File> mappingFiles, boolean invert) throws IOException {
+ Map<String, Mapping> mappings = new HashMap<>();
+ for (File mappingFile : mappingFiles) {
+ Map<String, Mapping> currentMappings = new HashMap<>(mappings);
+ int lineNumber = 0;
+ for (String line : Files.readAllLines(mappingFile.toPath(), StandardCharsets.UTF_8)) {
+ lineNumber++;
+ if (line.trim().startsWith("#") || line.trim().isEmpty()) continue;
+
+ String[] parts = line.split(" ");
+ if (parts.length < 2 || line.contains(";")) {
+ throw new IllegalArgumentException("Failed to parse line " + lineNumber + " in " + mappingFile.getPath() + ".");
+ }
+
+ Mapping mapping = currentMappings.get(parts[0]);
+ if (mapping == null) {
+ mapping = new Mapping();
+ mapping.oldName = mapping.newName = parts[0];
+ currentMappings.put(mapping.oldName, mapping);
+ mappings.put(mapping.newName, mapping);
+ }
+
+ if (parts.length == 2) {
+ // Class mapping
+ mappings.remove(mapping.newName);
+ mapping.newName = parts[1];
+ mappings.put(mapping.newName, mapping);
+ } else if (parts[1].endsWith("()")) {
+ // Method mapping
+ String name = parts[1].substring(0, parts[1].length() - 2);
+ String newName = parts[2].substring(0, parts[2].length() - 2);
+ String oldName = mapping.methods.remove(name);
+ if (oldName == null) oldName = name;
+ mapping.methods.put(oldName, newName);
+ } else {
+ // Field mapping
+ String name = parts[1];
+ String newName = parts[2];
+ String oldName = mapping.fields.remove(name);
+ if (oldName == null) oldName = name;
+ mapping.fields.put(oldName, newName);
+ }
+ }
+ }
+ if (invert) {
+ mappings.values().forEach(it -> {
+ String oldName = it.oldName;
+ it.oldName = it.newName;
+ it.newName = oldName;
+ it.fields = it.fields.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey));
+ it.methods = it.methods.entrySet().stream().collect(Collectors.toMap(Entry::getValue, Entry::getKey));
+ });
+ }
+ return mappings.entrySet().stream().collect(Collectors.toMap(e -> e.getValue().oldName, Entry::getValue));
+ }
+}