diff --git a/src/main/java/io/github/astrarre/sfu/SourceFixerUpper.java b/src/main/java/io/github/astrarre/sfu/SourceFixerUpper.java index 5245765..6a07b28 100644 --- a/src/main/java/io/github/astrarre/sfu/SourceFixerUpper.java +++ b/src/main/java/io/github/astrarre/sfu/SourceFixerUpper.java @@ -16,13 +16,11 @@ static SourceFixerUpper create() { SourceFixerUpper mappings(MappingTreeView tree, int srcNamespace, int dstNamespace); - SourceFixerUpper input(Path root); + SourceFixerUpper input(Path input, Path output); SourceFixerUpper sourcepath(Path root); SourceFixerUpper classpath(Path root); - SourceFixerUpper output(Path output); - void process() throws IOException; } diff --git a/src/main/java/io/github/astrarre/sfu/impl/SFUImpl.java b/src/main/java/io/github/astrarre/sfu/impl/SFUImpl.java index 0903348..0e6cb81 100644 --- a/src/main/java/io/github/astrarre/sfu/impl/SFUImpl.java +++ b/src/main/java/io/github/astrarre/sfu/impl/SFUImpl.java @@ -1,32 +1,49 @@ package io.github.astrarre.sfu.impl; import io.github.astrarre.sfu.SourceFixerUpper; -import java.io.IOException; +import java.io.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.net.URI; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import javax.tools.JavaFileObject; +import java.util.*; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.NestingKind; +import javax.tools.*; import net.fabricmc.mappingio.tree.MappingTreeView; import sfu_rpkg.com.sun.source.tree.CompilationUnitTree; -import sfu_rpkg.com.sun.source.util.JavacTask; import sfu_rpkg.com.sun.source.util.Trees; +import sfu_rpkg.com.sun.tools.javac.api.ClientCodeWrapper; +import sfu_rpkg.com.sun.tools.javac.api.JavacTaskImpl; import sfu_rpkg.com.sun.tools.javac.api.JavacTool; -import sfu_rpkg.com.sun.tools.javac.file.JavacFileManager; -import sfu_rpkg.com.sun.tools.javac.util.Context; public class SFUImpl implements SourceFixerUpper { + private static final MethodHandle UNWRAP; + + static { + try { + UNWRAP = MethodHandles.privateLookupIn(ClientCodeWrapper.class, MethodHandles.lookup()) + .findVirtual(ClientCodeWrapper.class, "unwrap", + MethodType.methodType(JavaFileObject.class, JavaFileObject.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + private Charset charset = Charset.defaultCharset(); private MappingTreeView tree; private int srcNamespace, dstNamespace; - private Path output; - private final List inputs, sourcepath, classpath; + private final Map inputs; + private final List sourcepath; + private final List classpath; public SFUImpl() { - this.inputs = new ArrayList<>(); + this.inputs = new LinkedHashMap<>(); this.sourcepath = new ArrayList<>(); this.classpath = new ArrayList<>(); } @@ -46,8 +63,8 @@ public SourceFixerUpper mappings(MappingTreeView tree, int srcNamespace, int dst } @Override - public SourceFixerUpper input(Path root) { - this.inputs.add(root); + public SourceFixerUpper input(Path input, Path output) { + this.inputs.put(input, output); return this; } @@ -63,39 +80,116 @@ public SourceFixerUpper classpath(Path root) { return this; } - @Override - public SourceFixerUpper output(Path output) { - this.output = output; - return this; - } - @Override public void process() throws IOException { - Context context = new Context(); - JavacFileManager jcFileManager = new JavacFileManager(context, true, charset); + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, charset); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); JavacTool javac = JavacTool.create(); - Path filePath = Files.list(inputs.get(0)).filter(Files::isRegularFile).findAny().get(); - - Iterable javaFiles = jcFileManager.getJavaFileObjects(filePath); - JavacTask jcTask = javac.getTask(null, - jcFileManager, + JavacTaskImpl task = (JavacTaskImpl) javac.getTask(null, + fileManager, + diagnostics, + List.of("-proc:none"), null, - List.of(), - null, - javaFiles); - Trees trees = Trees.instance(jcTask); - - Iterable codeResult = jcTask.parse(); - jcTask.analyze(); + inputs.entrySet().stream().map(path -> { + Iterator iterable = fileManager + .getJavaFileObjectsFromPaths(List.of(path.getKey())).iterator(); + JavaFileObject object = iterable.next(); + assert !iterable.hasNext(); + return new WJavaFileObject(object, path.getValue()); + }).toList()); + ClientCodeWrapper wrapper = ClientCodeWrapper.instance(task.getContext()); + Trees trees = Trees.instance(task); + + Iterable codeResult = task.parse(); + task.analyze(); for (CompilationUnitTree codeTree : codeResult) { - var jsv = new RangeCollectingVisitor(trees); - codeTree.accept(jsv, null); - StringBuilder builder = new StringBuilder(Files.readString(filePath)); - Remapper remapper = new Remapper(builder, jsv.members, jsv.types, tree, srcNamespace, dstNamespace); + var collector = new RangeCollectingVisitor(trees); + codeTree.accept(collector, null); + + WJavaFileObject sourceFile; + + try { + sourceFile = (WJavaFileObject) (JavaFileObject) UNWRAP.invokeExact(wrapper, codeTree.getSourceFile()); + } catch (Throwable e) { + throw new RuntimeException(e); + } + + StringBuilder builder = new StringBuilder(sourceFile.getCharContent(false)); + Remapper remapper = new Remapper(builder, collector.members, collector.types, tree, srcNamespace, + dstNamespace); remapper.apply(); - System.out.println(builder); + Files.createDirectories(sourceFile.output.getParent()); + Files.writeString(sourceFile.output, builder.toString(), charset); + } + } + + record WJavaFileObject(JavaFileObject object, Path output) implements JavaFileObject { + @Override + public Kind getKind() { + return object.getKind(); + } + + @Override + public boolean isNameCompatible(String simpleName, Kind kind) { + return object.isNameCompatible(simpleName, kind); + } + + @Override + public NestingKind getNestingKind() { + return object.getNestingKind(); + } + + @Override + public Modifier getAccessLevel() { + return object.getAccessLevel(); + } + + @Override + public URI toUri() { + return object.toUri(); + } + + @Override + public String getName() { + return object.getName(); + } + + @Override + public InputStream openInputStream() throws IOException { + return object.openInputStream(); + } + + @Override + public OutputStream openOutputStream() throws IOException { + return object.openOutputStream(); + } + + @Override + public Reader openReader(boolean ignoreEncodingErrors) throws IOException { + return object.openReader(ignoreEncodingErrors); + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { + return object.getCharContent(ignoreEncodingErrors); + } + + @Override + public Writer openWriter() throws IOException { + return object.openWriter(); + } + + @Override + public long getLastModified() { + return object.getLastModified(); + } + + @Override + public boolean delete() { + return object.delete(); } } } diff --git a/src/test/java/io/github/astrarre/sfu/test/RemappingTests.java b/src/test/java/io/github/astrarre/sfu/test/RemappingTests.java index 7140b36..ddea4c3 100644 --- a/src/test/java/io/github/astrarre/sfu/test/RemappingTests.java +++ b/src/test/java/io/github/astrarre/sfu/test/RemappingTests.java @@ -8,11 +8,10 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; -import java.util.Arrays; import net.fabricmc.mappingio.format.Tiny2Reader; import net.fabricmc.mappingio.tree.MemoryMappingTree; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.opentest4j.AssertionFailedError; public class RemappingTests { @@ -35,31 +34,33 @@ public void remap() throws IOException { Tiny2Reader.read(reader, tree); } - SourceFixerUpper.create() - .mappings(tree, tree.getNamespaceId("a"), tree.getNamespaceId("b")) - .input(original) - .output(output) - .process(); + SourceFixerUpper sfu = SourceFixerUpper.create() + .mappings(tree, tree.getNamespaceId("a"), tree.getNamespaceId("b")); - verifyDirsAreEqual(output, test); - verifyDirsAreEqual(test, output); + Files.walk(original).forEach(path -> { + if (Files.isRegularFile(path)) { + sfu.input(path, output.resolve(original.relativize(path))); + } + }); + + sfu.process(); + + verifyDirsAreEqual(output, test, false); + verifyDirsAreEqual(test, output, true); } } - private static void verifyDirsAreEqual(Path one, Path other) throws IOException { + private static void verifyDirsAreEqual(Path one, Path other, boolean flip) throws IOException { Files.walkFileTree(one, new SimpleFileVisitor<>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { FileVisitResult result = super.visitFile(file, attrs); + Path fileInOther = other.resolve(one.relativize(file)); - Path relativize = one.relativize(file); - Path fileInOther = other.resolve(relativize); - - byte[] otherBytes = Files.readAllBytes(fileInOther); - byte[] theseBytes = Files.readAllBytes(file); - - if (!Arrays.equals(otherBytes, theseBytes)) { - throw new AssertionFailedError(file + " is not equal to " + fileInOther); + if (flip) { + Assertions.assertEquals(Files.readString(file), Files.readString(fileInOther)); + } else { + Assertions.assertEquals(Files.readString(fileInOther), Files.readString(file)); } return result;