From 6706365356816c67f91e9b2af19bc98d82da5298 Mon Sep 17 00:00:00 2001 From: Logic <38597904+LogicFan@users.noreply.github.com> Date: Tue, 21 Sep 2021 21:01:22 -0400 Subject: [PATCH 1/6] Add new IO interface for TR --- .../fabricmc/tinyremapper/ClassInstance.java | 3 ++ .../fabricmc/tinyremapper/api/TrClass.java | 13 ++++++++ .../tinyremapper/api/TrEnvironment.java | 2 ++ .../tinyremapper/api/io/InputSupplier.java | 32 +++++++++++++++++++ .../tinyremapper/api/io/MappingSupplier.java | 16 ++++++++++ .../tinyremapper/api/io/OutputConsumer.java | 20 ++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java create mode 100644 src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java create mode 100644 src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java diff --git a/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java b/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java index 8c57af22..241f510a 100644 --- a/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java +++ b/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java @@ -870,6 +870,9 @@ public static String getMrjName(String clsName, int mrjVersion) { } } + /** + * @deprecated Please use {@link TrEnvironment#DEFAULT_MRJ_VERSION}. + */ public static final int MRJ_DEFAULT = -1; public static final String MRJ_PREFIX = "/META-INF/versions"; diff --git a/src/main/java/net/fabricmc/tinyremapper/api/TrClass.java b/src/main/java/net/fabricmc/tinyremapper/api/TrClass.java index e2f1f511..e5b777c6 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/TrClass.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/TrClass.java @@ -18,6 +18,8 @@ package net.fabricmc.tinyremapper.api; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Collection; import java.util.List; import java.util.function.Predicate; @@ -139,4 +141,15 @@ default boolean isRecord() { default boolean isModule() { return (getAccess() & Opcodes.ACC_MODULE) != 0; } + + static Path getPathInJar(String clsName, int mrjVersion) { + final String CLASS_SUFFIX = ".class"; + final String MRJ_PREFIX = "/META-INF/versions"; + + if (mrjVersion != TrEnvironment.DEFAULT_MRJ_VERSION) { + return Paths.get(MRJ_PREFIX, Integer.toString(mrjVersion), clsName + CLASS_SUFFIX); + } else { + return Paths.get(clsName + CLASS_SUFFIX); + } + } } diff --git a/src/main/java/net/fabricmc/tinyremapper/api/TrEnvironment.java b/src/main/java/net/fabricmc/tinyremapper/api/TrEnvironment.java index 55c37b05..371a1483 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/TrEnvironment.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/TrEnvironment.java @@ -40,4 +40,6 @@ default TrMethod getMethod(String owner, String name, String desc) { } void propagate(TrMember member, String newName); + + int DEFAULT_MRJ_VERSION = -1; } diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java new file mode 100644 index 00000000..86c9f9ad --- /dev/null +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java @@ -0,0 +1,32 @@ +package net.fabricmc.tinyremapper.api.io; + +import net.fabricmc.tinyremapper.api.TrClass; + +import java.io.IOException; +import java.nio.file.Path; + +public interface InputSupplier { + String getSource(); + void load(InputConsumer consumer) throws IOException; + + @FunctionalInterface + interface DataSupplier { + byte[] get() throws IOException; + } + + /** + * Only {@link InputConsumer#acceptResourceFile(Path, DataSupplier)} + * and {@link InputConsumer#acceptClassFile(Path, DataSupplier)} + * will be overwritten in the internal implementation. + */ + interface InputConsumer { + /** + * @deprecated Unstable API + */ + void acceptResourceFile(Path path, DataSupplier data); + void acceptClassFile(Path path, DataSupplier data); + default void acceptClassFile(String name, int mrjVersion, DataSupplier data) { + acceptClassFile(TrClass.getPathInJar(name, mrjVersion), data); + } + } +} diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java new file mode 100644 index 00000000..d22be347 --- /dev/null +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java @@ -0,0 +1,16 @@ +package net.fabricmc.tinyremapper.api.io; + +import java.io.IOException; + +public interface MappingSupplier { + String getSource(); + void load(MappingConsumer consumer) throws IOException; + + interface MappingConsumer { + void acceptClass(String srcName, String dstName); + void acceptMethod(String owner, String srcName, String desc, String dstName); + void acceptMethodArg(String owner, String srcName, String desc, int lvIndex, String dstName); + void acceptMethodVar(String owner, String srcName, String desc, int lvIndex, int startOpIdx, int asmIndex, String dstName); + void acceptField(String owner, String srcName, String desc, String dstName); + } +} diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java b/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java new file mode 100644 index 00000000..74cb97d8 --- /dev/null +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java @@ -0,0 +1,20 @@ +package net.fabricmc.tinyremapper.api.io; + +import net.fabricmc.tinyremapper.api.TrClass; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * @deprecated Unstable API + * Only {@link OutputConsumer#acceptResource(Path, byte[])} + * and {@link OutputConsumer#acceptClassFile(String, int, byte[])} + * will be called in the internal implementation. + */ +public interface OutputConsumer { + void acceptResource(Path path, byte[] data) throws IOException; + void acceptClassFile(Path path, byte[] data) throws IOException; + default void acceptClassFile(String name, int mrjVersion, byte[] data) throws IOException { + acceptClassFile(TrClass.getPathInJar(name, mrjVersion), data); + } +} From 5442df3a4fb82d55d162cfda8d79dd158784ec4c Mon Sep 17 00:00:00 2001 From: Logic <38597904+LogicFan@users.noreply.github.com> Date: Tue, 21 Sep 2021 21:26:06 -0400 Subject: [PATCH 2/6] add open & close method --- .../tinyremapper/api/io/InputSupplier.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java index 86c9f9ad..c5dad2ec 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java @@ -2,13 +2,31 @@ import net.fabricmc.tinyremapper.api.TrClass; +import java.io.Closeable; import java.io.IOException; import java.nio.file.Path; public interface InputSupplier { + /** + * Get the source of the input. Only useful for logging. + */ String getSource(); + + /** + * Must be called exactly once before {@link InputSupplier#load(InputConsumer)}. + */ + void open() throws IOException; + + /** + * Load files into {@link InputConsumer}. Only valid after {@link InputSupplier#open()}, before {@link InputSupplier#close()}. + */ void load(InputConsumer consumer) throws IOException; + /** + * Release any resources bind with this object. + */ + void close() throws IOException; + @FunctionalInterface interface DataSupplier { byte[] get() throws IOException; From 5259a26519943b8fd33c32173d85bf9b27642a71 Mon Sep 17 00:00:00 2001 From: Logic <38597904+LogicFan@users.noreply.github.com> Date: Thu, 23 Sep 2021 00:13:16 -0400 Subject: [PATCH 3/6] refactor input --- .../fabricmc/tinyremapper/ClassInstance.java | 9 +- .../fabricmc/tinyremapper/TinyRemapper.java | 207 ++++++++---------- .../tinyremapper/api/io/InputSupplier.java | 112 ++++++---- .../tinyremapper/api/io/MappingSupplier.java | 39 +++- .../tinyremapper/api/io/OutputConsumer.java | 34 ++- .../legacy/PathInputSupplier.java | 93 ++++++++ .../util/DirectoryInputSupplier.java | 105 +++++++++ .../tinyremapper/util/JarInputSupplier.java | 116 ++++++++++ 8 files changed, 529 insertions(+), 186 deletions(-) create mode 100644 src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java create mode 100644 src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java create mode 100644 src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java diff --git a/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java b/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java index 241f510a..b9e5b185 100644 --- a/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java +++ b/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java @@ -18,7 +18,6 @@ package net.fabricmc.tinyremapper; -import java.nio.file.Path; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -51,12 +50,12 @@ import net.fabricmc.tinyremapper.api.TrMethod; public final class ClassInstance implements TrClass { - ClassInstance(TinyRemapper tr, boolean isInput, InputTag[] inputTags, Path srcFile, byte[] data) { + ClassInstance(TinyRemapper tr, boolean isInput, InputTag[] inputTags, String source, byte[] data) { assert !isInput || data != null; this.tr = tr; this.isInput = isInput; this.inputTags = inputTags; - this.srcPath = srcFile; + this.source = source; this.data = data; this.mrjOrigin = this; } @@ -837,7 +836,7 @@ private static void addMatching(T member, String name, Stri ClassInstance constructMrjCopy(MrjState newContext) { // isInput should be false, since the MRJ copy should not be emitted - ClassInstance copy = new ClassInstance(tr, false, inputTags, srcPath, data); + ClassInstance copy = new ClassInstance(tr, false, inputTags, source, data); copy.init(mrjVersion, name, signature, superName, access, interfaces); copy.setContext(newContext); @@ -885,7 +884,7 @@ public static String getMrjName(String clsName, int mrjVersion) { final boolean isInput; private volatile InputTag[] inputTags; // cow input tag list, null for none - final Path srcPath; + final String source; byte[] data; private ClassInstance mrjOrigin; private final Map members = new HashMap<>(); // methods and fields are distinct due to their different desc separators diff --git a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java index 029dbe09..29256d14 100644 --- a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java +++ b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java @@ -19,14 +19,7 @@ package net.fabricmc.tinyremapper; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.FileSystem; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; @@ -49,10 +42,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; -import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.zip.ZipError; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -69,6 +60,8 @@ import net.fabricmc.tinyremapper.api.TrEnvironment; import net.fabricmc.tinyremapper.api.TrMember; import net.fabricmc.tinyremapper.api.TrMember.MemberType; +import net.fabricmc.tinyremapper.api.io.InputSupplier; +import net.fabricmc.tinyremapper.legacy.PathInputSupplier; public class TinyRemapper { public static class Builder { @@ -332,20 +325,20 @@ public InputTag createInputTag() { return ret; } - public void readInputs(final Path... inputs) { - readInputs(null, inputs); + public void readInputs(InputSupplier... inputs) { + readInputsAsync(inputs).join(); } - public void readInputs(InputTag tag, Path... inputs) { - read(inputs, true, tag).join(); + public void readInputs(InputTag tag, InputSupplier... inputs) { + readInputsAsync(tag, inputs).join(); } - public CompletableFuture readInputsAsync(Path... inputs) { + public CompletableFuture readInputsAsync(InputSupplier... inputs) { return readInputsAsync(null, inputs); } - public CompletableFuture readInputsAsync(InputTag tag, Path... inputs) { - CompletableFuture ret = read(inputs, true, tag); + public CompletableFuture readInputsAsync(InputTag tag, InputSupplier... inputs) { + CompletableFuture ret = readAsync0(true, tag, inputs); if (!ret.isDone()) { pendingReads.add(ret); @@ -356,12 +349,12 @@ public CompletableFuture readInputsAsync(InputTag tag, Path... inputs) { return ret; } - public void readClassPath(final Path... inputs) { - read(inputs, false, null).join(); + public void readClasspath(InputSupplier... inputs) { + readClasspathAsync(inputs).join(); } - public CompletableFuture readClassPathAsync(final Path... inputs) { - CompletableFuture ret = read(inputs, false, null); + public CompletableFuture readClasspathAsync(InputSupplier... inputs) { + CompletableFuture ret = readAsync0(false, null, inputs); if (!ret.isDone()) { pendingReads.add(ret); @@ -372,24 +365,29 @@ public CompletableFuture readClassPathAsync(final Path... inputs) { return ret; } - private CompletableFuture> read(Path[] inputs, boolean isInput, InputTag tag) { + private CompletableFuture readAsync0(boolean isInput, InputTag tag, InputSupplier... inputs) { InputTag[] tags = singleInputTags.get().get(tag); - List>> futures = new ArrayList<>(); - List fsToClose = Collections.synchronizedList(new ArrayList<>()); - for (Path input : inputs) { - futures.addAll(read(input, isInput, tags, true, fsToClose)); - } + Collection> futures = new ArrayList<>(); - CompletableFuture> ret; + for (InputSupplier input : inputs) { + String source = input.getSource(); - if (futures.isEmpty()) { - return CompletableFuture.completedFuture(Collections.emptyList()); - } else if (futures.size() == 1) { - ret = futures.get(0); - } else { - ret = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .thenApply(ignore -> futures.stream().flatMap(f -> f.join().stream()).collect(Collectors.toList())); + try { + input.open(); + futures.add(input.loadAsync(new InputSupplier.InputConsumer() { + @Override + public void acceptResourceFile(Path path, byte[] data) { } + + @Override + public void acceptClassFile(Path path, byte[] data) { + ClassInstance cls = analyze(isInput, tags, source, path, data); + if (cls != null) addClass(cls, readClasses, true); + } + }, threadPool)); + } catch (IOException e) { + System.out.println("Fail to open file " + source); + } } if (!dirty) { @@ -400,23 +398,61 @@ private CompletableFuture> read(Path[] inputs, boolean isInp } } - return ret.whenComplete((res, exc) -> { - for (FileSystem fs : fsToClose) { - try { - FileSystemHandler.close(fs); - } catch (IOException e) { - // ignore - } + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenRunAsync(() -> { + for (InputSupplier input : inputs) { + try { + input.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }, threadPool); + } + + /** Path overloads. **/ + public void readInputs(final Path... inputs) { + readInputsAsync(inputs).join(); + } + + public void readInputs(InputTag tag, Path... inputs) { + readInputsAsync(tag, inputs).join(); + } + + public CompletableFuture readInputsAsync(Path... inputs) { + return readInputsAsync(null, inputs); + } + + public CompletableFuture readInputsAsync(InputTag tag, Path... paths) { + InputSupplier[] inputs = new InputSupplier[paths.length]; + + for (int i = 0; i < paths.length; i += 1) { + try { + inputs[i] = new PathInputSupplier(paths[i]); + } catch (IOException e) { + throw new RuntimeException(e); } + } - if (res != null) { - for (ClassInstance node : res) { - addClass(node, readClasses, true); - } + return readInputsAsync(tag, inputs); + } + + public void readClassPath(final Path... paths) { + readClassPathAsync(paths).join(); + } + + public CompletableFuture readClassPathAsync(final Path... paths) { + InputSupplier[] inputs = new InputSupplier[paths.length]; + + for (int i = 0; i < paths.length; i += 1) { + try { + inputs[i] = new PathInputSupplier(paths[i]); + } catch (IOException e) { + throw new RuntimeException(e); } + } - assert dirty; - }); + return readClasspathAsync(inputs); } private static void addClass(ClassInstance cls, Map out, boolean isVersionAware) { @@ -438,7 +474,7 @@ private static void addClass(ClassInstance cls, Map out, } } else if (cls.isInput) { if (prev.isInput) { - System.out.printf("duplicate input class %s, from %s and %s%n", name, prev.srcPath, cls.srcPath); + System.out.printf("duplicate input class %s, from %s and %s%n", name, prev.source, cls.source); prev.addInputTags(cls.getInputTags()); return; } else if (out.replace(name, prev, cls)) { // cas with retry-loop on failure @@ -454,76 +490,6 @@ private static void addClass(ClassInstance cls, Map out, } } - private List>> read(final Path file, boolean isInput, InputTag[] tags, - boolean saveData, final List fsToClose) { - try { - return read(file, isInput, tags, file, saveData, fsToClose); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private List>> read(final Path file, boolean isInput, InputTag[] tags, final Path srcPath, - final boolean saveData, final List fsToClose) throws IOException { - List>> ret = new ArrayList<>(); - - Files.walkFileTree(file, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String name = file.getFileName().toString(); - - if (name.endsWith(".jar") - || name.endsWith(".zip") - || name.endsWith(".class")) { - ret.add(CompletableFuture.supplyAsync(new Supplier>() { - @Override - public List get() { - try { - return readFile(file, isInput, tags, srcPath, fsToClose); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } catch (IOException | ZipError e) { - throw new RuntimeException("Error reading file "+file, e); - } - } - }, threadPool)); - } - - return FileVisitResult.CONTINUE; - } - }); - - return ret; - } - - private List readFile(Path file, boolean isInput, InputTag[] tags, final Path srcPath, - List fsToClose) throws IOException, URISyntaxException { - List ret = new ArrayList(); - - if (file.toString().endsWith(".class")) { - ClassInstance res = analyze(isInput, tags, srcPath, file); - if (res != null) ret.add(res); - } else { - URI uri = new URI("jar:"+file.toUri().toString()); - FileSystem fs = FileSystemHandler.open(uri); - fsToClose.add(fs); - - Files.walkFileTree(fs.getPath("/"), new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (file.toString().endsWith(".class")) { - ClassInstance res = analyze(isInput, tags, srcPath, file); - if (res != null) ret.add(res); - } - - return FileVisitResult.CONTINUE; - } - }); - } - - return ret; - } - /** * Determine the MRJ version of the supplied class file and name. * @@ -556,13 +522,12 @@ private static int analyzeMrjVersion(Path file, String name) { return ClassInstance.MRJ_DEFAULT; } - private ClassInstance analyze(boolean isInput, InputTag[] tags, Path srcPath, Path file) throws IOException { - byte[] data = Files.readAllBytes(file); + private ClassInstance analyze(boolean isInput, InputTag[] tags, String source, Path file, byte[] data) { ClassReader reader = new ClassReader(data); if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) return null; // special attribute for module-info.class, can't be a regular class - final ClassInstance ret = new ClassInstance(this, isInput, tags, srcPath, isInput ? data : null); + final ClassInstance ret = new ClassInstance(this, isInput, tags, source, isInput ? data : null); reader.accept(new ClassVisitor(Opcodes.ASM9) { @Override diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java index c5dad2ec..36444a60 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java @@ -1,50 +1,76 @@ -package net.fabricmc.tinyremapper.api.io; +/* + * Copyright (c) 2016, 2018, Player, asie + * Copyright (c) 2021, FabricMC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ -import net.fabricmc.tinyremapper.api.TrClass; +package net.fabricmc.tinyremapper.api.io; -import java.io.Closeable; import java.io.IOException; import java.nio.file.Path; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ForkJoinPool; + +import net.fabricmc.tinyremapper.api.TrClass; public interface InputSupplier { - /** - * Get the source of the input. Only useful for logging. - */ - String getSource(); - - /** - * Must be called exactly once before {@link InputSupplier#load(InputConsumer)}. - */ - void open() throws IOException; - - /** - * Load files into {@link InputConsumer}. Only valid after {@link InputSupplier#open()}, before {@link InputSupplier#close()}. - */ - void load(InputConsumer consumer) throws IOException; - - /** - * Release any resources bind with this object. - */ - void close() throws IOException; - - @FunctionalInterface - interface DataSupplier { - byte[] get() throws IOException; - } - - /** - * Only {@link InputConsumer#acceptResourceFile(Path, DataSupplier)} - * and {@link InputConsumer#acceptClassFile(Path, DataSupplier)} - * will be overwritten in the internal implementation. - */ - interface InputConsumer { - /** - * @deprecated Unstable API - */ - void acceptResourceFile(Path path, DataSupplier data); - void acceptClassFile(Path path, DataSupplier data); - default void acceptClassFile(String name, int mrjVersion, DataSupplier data) { - acceptClassFile(TrClass.getPathInJar(name, mrjVersion), data); - } - } + /** + * Only {@link InputConsumer#acceptResourceFile(Path, byte[])} + * and {@link InputConsumer#acceptClassFile(Path, byte[])} + * will be overwritten in the internal implementation. + */ + interface InputConsumer { + /** + * @deprecated Unstable API + */ + void acceptResourceFile(Path path, byte[] data); + void acceptClassFile(Path path, byte[] data); + default void acceptClassFile(String name, int mrjVersion, byte[] data) { + this.acceptClassFile(TrClass.getPathInJar(name, mrjVersion), data); + } + } + + /** + * Flags to indicate which type of file should be read. Only useful for sub-classes. + */ + enum FileType { + CLASS_FILE, RESOURCE_FILE + } + + /** + * Get the source of the input. Only useful for logging. + */ + String getSource(); + + /** + * Must be called exactly once before {@link InputSupplier#load(InputConsumer)}. + */ + void open() throws IOException; + + /** + * Load files into {@link InputConsumer}. Only valid after {@link InputSupplier#open()}, before {@link InputSupplier#close()}. + */ + CompletableFuture loadAsync(InputConsumer consumer, Executor executor); + default void load(InputConsumer consumer) { + // ForkJoinPool.commonPool() is the default executor in Java. + loadAsync(consumer, ForkJoinPool.commonPool()).join(); + } + + /** + * Release any resources binding with this object. + */ + void close() throws IOException; } diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java index d22be347..cd35ce07 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java @@ -1,16 +1,37 @@ +/* + * Copyright (c) 2016, 2018, Player, asie + * Copyright (c) 2021, FabricMC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + package net.fabricmc.tinyremapper.api.io; import java.io.IOException; +/** + * @deprecated Not Implemented. + */ public interface MappingSupplier { - String getSource(); - void load(MappingConsumer consumer) throws IOException; + String getSource(); + void load(MappingConsumer consumer) throws IOException; - interface MappingConsumer { - void acceptClass(String srcName, String dstName); - void acceptMethod(String owner, String srcName, String desc, String dstName); - void acceptMethodArg(String owner, String srcName, String desc, int lvIndex, String dstName); - void acceptMethodVar(String owner, String srcName, String desc, int lvIndex, int startOpIdx, int asmIndex, String dstName); - void acceptField(String owner, String srcName, String desc, String dstName); - } + interface MappingConsumer { + void acceptClass(String srcName, String dstName); + void acceptMethod(String owner, String srcName, String desc, String dstName); + void acceptMethodArg(String owner, String srcName, String desc, int lvIndex, String dstName); + void acceptMethodVar(String owner, String srcName, String desc, int lvIndex, int startOpIdx, int asmIndex, String dstName); + void acceptField(String owner, String srcName, String desc, String dstName); + } } diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java b/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java index 74cb97d8..4f2104a0 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java @@ -1,20 +1,38 @@ -package net.fabricmc.tinyremapper.api.io; +/* + * Copyright (c) 2016, 2018, Player, asie + * Copyright (c) 2021, FabricMC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ -import net.fabricmc.tinyremapper.api.TrClass; +package net.fabricmc.tinyremapper.api.io; import java.io.IOException; import java.nio.file.Path; +import net.fabricmc.tinyremapper.api.TrClass; + /** - * @deprecated Unstable API + * @deprecated Not Implemented. * Only {@link OutputConsumer#acceptResource(Path, byte[])} * and {@link OutputConsumer#acceptClassFile(String, int, byte[])} * will be called in the internal implementation. */ public interface OutputConsumer { - void acceptResource(Path path, byte[] data) throws IOException; - void acceptClassFile(Path path, byte[] data) throws IOException; - default void acceptClassFile(String name, int mrjVersion, byte[] data) throws IOException { - acceptClassFile(TrClass.getPathInJar(name, mrjVersion), data); - } + void acceptResource(Path path, byte[] data) throws IOException; + void acceptClassFile(Path path, byte[] data) throws IOException; + default void acceptClassFile(String name, int mrjVersion, byte[] data) throws IOException { + acceptClassFile(TrClass.getPathInJar(name, mrjVersion), data); + } } diff --git a/src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java new file mode 100644 index 00000000..917b45f5 --- /dev/null +++ b/src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016, 2018, Player, asie + * Copyright (c) 2021, FabricMC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package net.fabricmc.tinyremapper.legacy; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import net.fabricmc.tinyremapper.api.io.InputSupplier; +import net.fabricmc.tinyremapper.util.DirectoryInputSupplier; +import net.fabricmc.tinyremapper.util.JarInputSupplier; + +/** + * This mimic the legacy behaviour of reading input. + */ +public final class PathInputSupplier implements InputSupplier { + private final Path path; + private final List inputs; + + public PathInputSupplier(Path path) throws IOException { + this.path = path; + this.inputs = new ArrayList<>(); + + if (Files.isDirectory(path)) this.inputs.add(new DirectoryInputSupplier(path, EnumSet.of(FileType.CLASS_FILE))); + + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String fileName = file.getFileName().toString(); + + if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) { + PathInputSupplier.this.inputs.add(new JarInputSupplier(file, EnumSet.of(FileType.CLASS_FILE))); + } + + return FileVisitResult.CONTINUE; + } + }); + } + + @Override + public String getSource() { + return path.toString(); + } + + @Override + public void open() throws IOException { + for (InputSupplier input : this.inputs) { + input.open(); + } + } + + @Override + public CompletableFuture loadAsync(InputConsumer consumer, Executor executor) { + CompletableFuture[] futures = new CompletableFuture[this.inputs.size()]; + + for (int i = 0; i < this.inputs.size(); i += 1) { + futures[i] = this.inputs.get(i).loadAsync(consumer, executor); + } + + return CompletableFuture.allOf(futures); + } + + @Override + public void close() throws IOException { + for (InputSupplier input : this.inputs) { + input.close(); + } + } +} diff --git a/src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java new file mode 100644 index 00000000..e33ca20a --- /dev/null +++ b/src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016, 2018, Player, asie + * Copyright (c) 2021, FabricMC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package net.fabricmc.tinyremapper.util; + +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import net.fabricmc.tinyremapper.api.io.InputSupplier; + +public final class DirectoryInputSupplier implements InputSupplier { + private final Path directory; + private final Set types; + + public DirectoryInputSupplier(Path directory) throws IOException { + this(directory, EnumSet.allOf(FileType.class)); + } + + public DirectoryInputSupplier(Path directory, Set types) throws IOException { + if (!Files.exists(directory)) throw new IOException("Path " + directory + " does not exists."); + if (!Files.isDirectory(directory)) throw new IOException("Path " + directory + " is not a directory."); + + this.directory = directory; + this.types = types; + } + + @Override + public String getSource() { + return "Directory:" + directory; + } + + @Override + public void open() { } + + @Override + public CompletableFuture loadAsync(InputConsumer consumer, Executor executor) { + Collection> futures = new ArrayList<>(); + + try { + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path relativePath = directory.toAbsolutePath().relativize(file.toAbsolutePath()); + String fileName = file.getFileName().toString(); + + if (fileName.endsWith(".class")) { + if (types.contains(FileType.CLASS_FILE)) { + futures.add(CompletableFuture.runAsync(() -> { + try { + consumer.acceptClassFile(relativePath, Files.readAllBytes(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, executor)); + } + } else { + if (types.contains(FileType.RESOURCE_FILE)) { + futures.add(CompletableFuture.runAsync(() -> { + try { + consumer.acceptResourceFile(relativePath, Files.readAllBytes(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, executor)); + } + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + } + + @Override + public void close() { } +} diff --git a/src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java new file mode 100644 index 00000000..d948e2d8 --- /dev/null +++ b/src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016, 2018, Player, asie + * Copyright (c) 2021, FabricMC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package net.fabricmc.tinyremapper.util; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystem; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +import net.fabricmc.tinyremapper.FileSystemHandler; +import net.fabricmc.tinyremapper.api.io.InputSupplier; + +public class JarInputSupplier implements InputSupplier { + private final Path jarPath; + private final Set types; + + private FileSystem jarFileSystem; + + public JarInputSupplier(Path path) { + this(path, EnumSet.allOf(FileType.class)); + } + + public JarInputSupplier(Path path, Set types) { + this.jarPath = path; + this.types = types; + this.jarFileSystem = null; + } + + @Override + public String getSource() { + return "Jar:" + this.jarPath; + } + + @Override + public void open() throws IOException { + try { + this.jarFileSystem = FileSystemHandler.open(new URI("jar:" + this.jarPath.toUri())); + } catch (URISyntaxException e) { + throw new IOException("Cannot open Jar file", e); + } + } + + @Override + public CompletableFuture loadAsync(InputSupplier.InputConsumer consumer, Executor executor) { + Collection> futures = new ArrayList<>(); + + try { + Files.walkFileTree(this.jarFileSystem.getPath("/"), new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String fileName = file.getFileName().toString(); + + if (fileName.endsWith(".class")) { + if (types.contains(FileType.CLASS_FILE)) { + futures.add(CompletableFuture.runAsync(() -> { + try { + consumer.acceptClassFile(file, Files.readAllBytes(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, executor)); + } + } else { + if (types.contains(FileType.RESOURCE_FILE)) { + futures.add(CompletableFuture.runAsync(() -> { + try { + consumer.acceptResourceFile(file, Files.readAllBytes(file)); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, executor)); + } + } + + return FileVisitResult.CONTINUE; + } + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + } + + @Override + public void close() throws IOException { + FileSystemHandler.close(this.jarFileSystem); + } +} From 89420a0eb12e3af028070f777aac458f6d4fafb3 Mon Sep 17 00:00:00 2001 From: Logic <38597904+LogicFan@users.noreply.github.com> Date: Thu, 23 Sep 2021 16:57:53 -0400 Subject: [PATCH 4/6] Add `@deprecated` notation for Unstable API --- .../fabricmc/tinyremapper/TinyRemapper.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java index 29256d14..33057404 100644 --- a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java +++ b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java @@ -325,18 +325,30 @@ public InputTag createInputTag() { return ret; } + /** + * @deprecated Unstable API + */ public void readInputs(InputSupplier... inputs) { readInputsAsync(inputs).join(); } + /** + * @deprecated Unstable API + */ public void readInputs(InputTag tag, InputSupplier... inputs) { readInputsAsync(tag, inputs).join(); } + /** + * @deprecated Unstable API + */ public CompletableFuture readInputsAsync(InputSupplier... inputs) { return readInputsAsync(null, inputs); } + /** + * @deprecated Unstable API + */ public CompletableFuture readInputsAsync(InputTag tag, InputSupplier... inputs) { CompletableFuture ret = readAsync0(true, tag, inputs); @@ -349,10 +361,16 @@ public CompletableFuture readInputsAsync(InputTag tag, InputSupplier... input return ret; } + /** + * @deprecated Unstable API + */ public void readClasspath(InputSupplier... inputs) { readClasspathAsync(inputs).join(); } + /** + * @deprecated Unstable API + */ public CompletableFuture readClasspathAsync(InputSupplier... inputs) { CompletableFuture ret = readAsync0(false, null, inputs); From f03047f187ad11253b5e9dfe5cbf48797fca00d9 Mon Sep 17 00:00:00 2001 From: Logic <38597904+LogicFan@users.noreply.github.com> Date: Thu, 23 Sep 2021 17:11:54 -0400 Subject: [PATCH 5/6] add deprecated notation --- src/main/java/net/fabricmc/tinyremapper/ClassInstance.java | 1 + src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java | 6 ++++++ .../net/fabricmc/tinyremapper/api/io/InputSupplier.java | 1 + .../net/fabricmc/tinyremapper/api/io/MappingSupplier.java | 1 + .../net/fabricmc/tinyremapper/api/io/OutputConsumer.java | 1 + 5 files changed, 10 insertions(+) diff --git a/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java b/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java index b9e5b185..d9e624b3 100644 --- a/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java +++ b/src/main/java/net/fabricmc/tinyremapper/ClassInstance.java @@ -872,6 +872,7 @@ public static String getMrjName(String clsName, int mrjVersion) { /** * @deprecated Please use {@link TrEnvironment#DEFAULT_MRJ_VERSION}. */ + @Deprecated public static final int MRJ_DEFAULT = -1; public static final String MRJ_PREFIX = "/META-INF/versions"; diff --git a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java index 33057404..0268fcbf 100644 --- a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java +++ b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java @@ -328,6 +328,7 @@ public InputTag createInputTag() { /** * @deprecated Unstable API */ + @Deprecated public void readInputs(InputSupplier... inputs) { readInputsAsync(inputs).join(); } @@ -335,6 +336,7 @@ public void readInputs(InputSupplier... inputs) { /** * @deprecated Unstable API */ + @Deprecated public void readInputs(InputTag tag, InputSupplier... inputs) { readInputsAsync(tag, inputs).join(); } @@ -342,6 +344,7 @@ public void readInputs(InputTag tag, InputSupplier... inputs) { /** * @deprecated Unstable API */ + @Deprecated public CompletableFuture readInputsAsync(InputSupplier... inputs) { return readInputsAsync(null, inputs); } @@ -349,6 +352,7 @@ public CompletableFuture readInputsAsync(InputSupplier... inputs) { /** * @deprecated Unstable API */ + @Deprecated public CompletableFuture readInputsAsync(InputTag tag, InputSupplier... inputs) { CompletableFuture ret = readAsync0(true, tag, inputs); @@ -364,6 +368,7 @@ public CompletableFuture readInputsAsync(InputTag tag, InputSupplier... input /** * @deprecated Unstable API */ + @Deprecated public void readClasspath(InputSupplier... inputs) { readClasspathAsync(inputs).join(); } @@ -371,6 +376,7 @@ public void readClasspath(InputSupplier... inputs) { /** * @deprecated Unstable API */ + @Deprecated public CompletableFuture readClasspathAsync(InputSupplier... inputs) { CompletableFuture ret = readAsync0(false, null, inputs); diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java index 36444a60..ad071b6b 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java @@ -36,6 +36,7 @@ interface InputConsumer { /** * @deprecated Unstable API */ + @Deprecated void acceptResourceFile(Path path, byte[] data); void acceptClassFile(Path path, byte[] data); default void acceptClassFile(String name, int mrjVersion, byte[] data) { diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java index cd35ce07..25d2c946 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/MappingSupplier.java @@ -23,6 +23,7 @@ /** * @deprecated Not Implemented. */ +@Deprecated public interface MappingSupplier { String getSource(); void load(MappingConsumer consumer) throws IOException; diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java b/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java index 4f2104a0..eb98d441 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/OutputConsumer.java @@ -29,6 +29,7 @@ * and {@link OutputConsumer#acceptClassFile(String, int, byte[])} * will be called in the internal implementation. */ +@Deprecated public interface OutputConsumer { void acceptResource(Path path, byte[] data) throws IOException; void acceptClassFile(Path path, byte[] data) throws IOException; From 79d10411254f2122519c372e9e1fda978cd4f6f3 Mon Sep 17 00:00:00 2001 From: Player Date: Fri, 24 Sep 2021 01:56:45 +0200 Subject: [PATCH 6/6] Simplify InputSupplier, cleanup --- .../tinyremapper/FileSystemHandler.java | 10 ++ .../fabricmc/tinyremapper/TinyRemapper.java | 66 +++++------ .../tinyremapper/api/io/InputSupplier.java | 46 +------- .../legacy/PathInputSupplier.java | 93 --------------- .../util/DirectoryInputSupplier.java | 74 ++---------- .../tinyremapper/util/JarInputSupplier.java | 82 ++------------ .../tinyremapper/util/PathInputSupplier.java | 107 ++++++++++++++++++ .../tinyremapper/TinyRemapperTest.java | 14 ++- 8 files changed, 176 insertions(+), 316 deletions(-) delete mode 100644 src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java create mode 100644 src/main/java/net/fabricmc/tinyremapper/util/PathInputSupplier.java diff --git a/src/main/java/net/fabricmc/tinyremapper/FileSystemHandler.java b/src/main/java/net/fabricmc/tinyremapper/FileSystemHandler.java index 4cb6eaac..927b1667 100644 --- a/src/main/java/net/fabricmc/tinyremapper/FileSystemHandler.java +++ b/src/main/java/net/fabricmc/tinyremapper/FileSystemHandler.java @@ -20,10 +20,12 @@ import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.nio.file.FileSystem; import java.nio.file.FileSystemAlreadyExistsException; import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; +import java.nio.file.Path; import java.util.Collections; import java.util.IdentityHashMap; import java.util.Map; @@ -36,6 +38,14 @@ * invocations are mirrored. */ public final class FileSystemHandler { + public static synchronized FileSystem open(Path path) throws IOException { + try { + return open(new URI("jar:" + path.toUri())); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + public static synchronized FileSystem open(URI uri) throws IOException { boolean opened = false; FileSystem ret = null; diff --git a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java index 0268fcbf..e61b3930 100644 --- a/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java +++ b/src/main/java/net/fabricmc/tinyremapper/TinyRemapper.java @@ -19,6 +19,7 @@ package net.fabricmc.tinyremapper; import java.io.IOException; +import java.io.UncheckedIOException; import java.nio.file.Path; import java.util.ArrayDeque; import java.util.ArrayList; @@ -42,6 +43,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -61,7 +64,7 @@ import net.fabricmc.tinyremapper.api.TrMember; import net.fabricmc.tinyremapper.api.TrMember.MemberType; import net.fabricmc.tinyremapper.api.io.InputSupplier; -import net.fabricmc.tinyremapper.legacy.PathInputSupplier; +import net.fabricmc.tinyremapper.util.PathInputSupplier; public class TinyRemapper { public static class Builder { @@ -397,21 +400,13 @@ private CompletableFuture readAsync0(boolean isInput, InputTag tag, InputSupp for (InputSupplier input : inputs) { String source = input.getSource(); - try { - input.open(); - futures.add(input.loadAsync(new InputSupplier.InputConsumer() { - @Override - public void acceptResourceFile(Path path, byte[] data) { } - - @Override - public void acceptClassFile(Path path, byte[] data) { - ClassInstance cls = analyze(isInput, tags, source, path, data); - if (cls != null) addClass(cls, readClasses, true); - } - }, threadPool)); - } catch (IOException e) { - System.out.println("Fail to open file " + source); - } + futures.add(CompletableFuture.supplyAsync(() -> { + try { + return input.read(classFileNameFilter, threadPool, (path, data) -> analyze(isInput, tags, source, path, data)); + } catch (IOException e) { + throw new UncheckedIOException("Error reading "+input, e); + } + }).thenCompose(Function.identity())); } if (!dirty) { @@ -422,16 +417,7 @@ public void acceptClassFile(Path path, byte[] data) { } } - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .thenRunAsync(() -> { - for (InputSupplier input : inputs) { - try { - input.close(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }, threadPool); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } /** Path overloads. **/ @@ -450,7 +436,7 @@ public CompletableFuture readInputsAsync(Path... inputs) { public CompletableFuture readInputsAsync(InputTag tag, Path... paths) { InputSupplier[] inputs = new InputSupplier[paths.length]; - for (int i = 0; i < paths.length; i += 1) { + for (int i = 0; i < paths.length; i++) { try { inputs[i] = new PathInputSupplier(paths[i]); } catch (IOException e) { @@ -543,21 +529,21 @@ private static int analyzeMrjVersion(Path file, String name) { } } - return ClassInstance.MRJ_DEFAULT; + return TrEnvironment.DEFAULT_MRJ_VERSION; } - private ClassInstance analyze(boolean isInput, InputTag[] tags, String source, Path file, byte[] data) { + private void analyze(boolean isInput, InputTag[] tags, String source, Path file, byte[] data) { ClassReader reader = new ClassReader(data); - if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) return null; // special attribute for module-info.class, can't be a regular class + if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) return; // special attribute for module-info.class, can't be a regular class - final ClassInstance ret = new ClassInstance(this, isInput, tags, source, isInput ? data : null); + final ClassInstance cls = new ClassInstance(this, isInput, tags, source, isInput ? data : null); reader.accept(new ClassVisitor(Opcodes.ASM9) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { int mrjVersion = analyzeMrjVersion(file, name); - ret.init(mrjVersion, name, signature, superName, access, interfaces); + cls.init(mrjVersion, name, signature, superName, access, interfaces); for (int i = analyzeVisitors.size() - 1; i >= 0; i--) { cv = analyzeVisitors.get(i).insertAnalyzeVisitor(mrjVersion, name, cv); @@ -568,22 +554,22 @@ public void visit(int version, int access, String name, String signature, String @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - MemberInstance prev = ret.addMember(new MemberInstance(TrMember.MemberType.METHOD, ret, name, desc, access, ret.getMembers().size())); - if (prev != null) throw new RuntimeException(String.format("duplicate method %s/%s%s in inputs", ret.getName(), name, desc)); + MemberInstance prev = cls.addMember(new MemberInstance(TrMember.MemberType.METHOD, cls, name, desc, access, cls.getMembers().size())); + if (prev != null) throw new RuntimeException(String.format("duplicate method %s/%s%s in inputs", cls.getName(), name, desc)); return super.visitMethod(access, name, desc, signature, exceptions); } @Override public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { - MemberInstance prev = ret.addMember(new MemberInstance(TrMember.MemberType.FIELD, ret, name, desc, access, ret.getMembers().size())); - if (prev != null) throw new RuntimeException(String.format("duplicate field %s/%s;;%s in inputs", ret.getName(), name, desc)); + MemberInstance prev = cls.addMember(new MemberInstance(TrMember.MemberType.FIELD, cls, name, desc, access, cls.getMembers().size())); + if (prev != null) throw new RuntimeException(String.format("duplicate field %s/%s;;%s in inputs", cls.getName(), name, desc)); return super.visitField(access, name, desc, signature, value); } }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES | ClassReader.SKIP_CODE); - return ret; + addClass(cls, readClasses, true); } private void loadMappings() { @@ -1279,7 +1265,9 @@ public void propagate(TrMember m, String newName) { volatile boolean dirty = true; } - private final boolean check = false; + private static final boolean check = false; + + private static final Predicate classFileNameFilter = name -> name.endsWith(".class"); private final boolean keepInputData; final Set forcePropagation; @@ -1306,7 +1294,7 @@ public void propagate(TrMember m, String newName) { final List> pendingReads = new ArrayList<>(); // reads that need to be waited for before continuing processing (assumes lack of external waiting) final Map readClasses = new ConcurrentHashMap<>(); // classes being potentially concurrently read, to be transferred into unsynchronized classes later - final MrjState defaultState = new MrjState(this, ClassInstance.MRJ_DEFAULT); + final MrjState defaultState = new MrjState(this, TrEnvironment.DEFAULT_MRJ_VERSION); final Map mrjStates = new HashMap<>(); { diff --git a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java index ad071b6b..48b64aaf 100644 --- a/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/api/io/InputSupplier.java @@ -22,56 +22,20 @@ import java.nio.file.Path; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; - -import net.fabricmc.tinyremapper.api.TrClass; +import java.util.function.Predicate; public interface InputSupplier { - /** - * Only {@link InputConsumer#acceptResourceFile(Path, byte[])} - * and {@link InputConsumer#acceptClassFile(Path, byte[])} - * will be overwritten in the internal implementation. - */ interface InputConsumer { - /** - * @deprecated Unstable API - */ - @Deprecated - void acceptResourceFile(Path path, byte[] data); - void acceptClassFile(Path path, byte[] data); - default void acceptClassFile(String name, int mrjVersion, byte[] data) { - this.acceptClassFile(TrClass.getPathInJar(name, mrjVersion), data); - } - } - - /** - * Flags to indicate which type of file should be read. Only useful for sub-classes. - */ - enum FileType { - CLASS_FILE, RESOURCE_FILE + void accept(Path path, byte[] data); } /** - * Get the source of the input. Only useful for logging. + * Get the source of the input. */ String getSource(); /** - * Must be called exactly once before {@link InputSupplier#load(InputConsumer)}. - */ - void open() throws IOException; - - /** - * Load files into {@link InputConsumer}. Only valid after {@link InputSupplier#open()}, before {@link InputSupplier#close()}. - */ - CompletableFuture loadAsync(InputConsumer consumer, Executor executor); - default void load(InputConsumer consumer) { - // ForkJoinPool.commonPool() is the default executor in Java. - loadAsync(consumer, ForkJoinPool.commonPool()).join(); - } - - /** - * Release any resources binding with this object. + * Load matching files into {@link InputConsumer}. */ - void close() throws IOException; + CompletableFuture read(Predicate fileNameFilter, Executor executor, InputConsumer consumer) throws IOException; } diff --git a/src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java deleted file mode 100644 index 917b45f5..00000000 --- a/src/main/java/net/fabricmc/tinyremapper/legacy/PathInputSupplier.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2016, 2018, Player, asie - * Copyright (c) 2021, FabricMC - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package net.fabricmc.tinyremapper.legacy; - -import java.io.IOException; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; - -import net.fabricmc.tinyremapper.api.io.InputSupplier; -import net.fabricmc.tinyremapper.util.DirectoryInputSupplier; -import net.fabricmc.tinyremapper.util.JarInputSupplier; - -/** - * This mimic the legacy behaviour of reading input. - */ -public final class PathInputSupplier implements InputSupplier { - private final Path path; - private final List inputs; - - public PathInputSupplier(Path path) throws IOException { - this.path = path; - this.inputs = new ArrayList<>(); - - if (Files.isDirectory(path)) this.inputs.add(new DirectoryInputSupplier(path, EnumSet.of(FileType.CLASS_FILE))); - - Files.walkFileTree(path, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String fileName = file.getFileName().toString(); - - if (fileName.endsWith(".zip") || fileName.endsWith(".jar")) { - PathInputSupplier.this.inputs.add(new JarInputSupplier(file, EnumSet.of(FileType.CLASS_FILE))); - } - - return FileVisitResult.CONTINUE; - } - }); - } - - @Override - public String getSource() { - return path.toString(); - } - - @Override - public void open() throws IOException { - for (InputSupplier input : this.inputs) { - input.open(); - } - } - - @Override - public CompletableFuture loadAsync(InputConsumer consumer, Executor executor) { - CompletableFuture[] futures = new CompletableFuture[this.inputs.size()]; - - for (int i = 0; i < this.inputs.size(); i += 1) { - futures[i] = this.inputs.get(i).loadAsync(consumer, executor); - } - - return CompletableFuture.allOf(futures); - } - - @Override - public void close() throws IOException { - for (InputSupplier input : this.inputs) { - input.close(); - } - } -} diff --git a/src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java index e33ca20a..75eb630b 100644 --- a/src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/util/DirectoryInputSupplier.java @@ -19,87 +19,35 @@ package net.fabricmc.tinyremapper.util; import java.io.IOException; -import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.function.Predicate; import net.fabricmc.tinyremapper.api.io.InputSupplier; public final class DirectoryInputSupplier implements InputSupplier { - private final Path directory; - private final Set types; - public DirectoryInputSupplier(Path directory) throws IOException { - this(directory, EnumSet.allOf(FileType.class)); - } - - public DirectoryInputSupplier(Path directory, Set types) throws IOException { - if (!Files.exists(directory)) throw new IOException("Path " + directory + " does not exists."); - if (!Files.isDirectory(directory)) throw new IOException("Path " + directory + " is not a directory."); - - this.directory = directory; - this.types = types; + this.path = directory; } @Override public String getSource() { - return "Directory:" + directory; + return path.toString(); } @Override - public void open() { } - - @Override - public CompletableFuture loadAsync(InputConsumer consumer, Executor executor) { - Collection> futures = new ArrayList<>(); + public CompletableFuture read(Predicate fileNameFilter, Executor executor, InputConsumer consumer) throws IOException { + if (!Files.isDirectory(path)) throw new IOException("not a directory: "+path); - try { - Files.walkFileTree(directory, new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Path relativePath = directory.toAbsolutePath().relativize(file.toAbsolutePath()); - String fileName = file.getFileName().toString(); - - if (fileName.endsWith(".class")) { - if (types.contains(FileType.CLASS_FILE)) { - futures.add(CompletableFuture.runAsync(() -> { - try { - consumer.acceptClassFile(relativePath, Files.readAllBytes(file)); - } catch (IOException e) { - throw new RuntimeException(e); - } - }, executor)); - } - } else { - if (types.contains(FileType.RESOURCE_FILE)) { - futures.add(CompletableFuture.runAsync(() -> { - try { - consumer.acceptResourceFile(relativePath, Files.readAllBytes(file)); - } catch (IOException e) { - throw new RuntimeException(e); - } - }, executor)); - } - } - - return FileVisitResult.CONTINUE; - } - }); - } catch (IOException e) { - throw new RuntimeException(e); - } - - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + return PathInputSupplier.read(path, fileNameFilter, false, executor, consumer); } @Override - public void close() { } + public String toString() { + return String.format("dir %s", path); + } + + private final Path path; } diff --git a/src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java index d948e2d8..ed1262a1 100644 --- a/src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java +++ b/src/main/java/net/fabricmc/tinyremapper/util/JarInputSupplier.java @@ -19,98 +19,32 @@ package net.fabricmc.tinyremapper.util; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.FileSystem; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumSet; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.function.Predicate; -import net.fabricmc.tinyremapper.FileSystemHandler; import net.fabricmc.tinyremapper.api.io.InputSupplier; public class JarInputSupplier implements InputSupplier { - private final Path jarPath; - private final Set types; - - private FileSystem jarFileSystem; - public JarInputSupplier(Path path) { - this(path, EnumSet.allOf(FileType.class)); - } - - public JarInputSupplier(Path path, Set types) { - this.jarPath = path; - this.types = types; - this.jarFileSystem = null; + this.path = path; } @Override public String getSource() { - return "Jar:" + this.jarPath; + return path.toString(); } @Override - public void open() throws IOException { - try { - this.jarFileSystem = FileSystemHandler.open(new URI("jar:" + this.jarPath.toUri())); - } catch (URISyntaxException e) { - throw new IOException("Cannot open Jar file", e); - } + public CompletableFuture read(Predicate fileNameFilter, Executor executor, InputConsumer consumer) throws IOException { + return PathInputSupplier.readZip(path, fileNameFilter, executor, consumer); } @Override - public CompletableFuture loadAsync(InputSupplier.InputConsumer consumer, Executor executor) { - Collection> futures = new ArrayList<>(); - - try { - Files.walkFileTree(this.jarFileSystem.getPath("/"), new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String fileName = file.getFileName().toString(); - - if (fileName.endsWith(".class")) { - if (types.contains(FileType.CLASS_FILE)) { - futures.add(CompletableFuture.runAsync(() -> { - try { - consumer.acceptClassFile(file, Files.readAllBytes(file)); - } catch (IOException e) { - throw new RuntimeException(e); - } - }, executor)); - } - } else { - if (types.contains(FileType.RESOURCE_FILE)) { - futures.add(CompletableFuture.runAsync(() -> { - try { - consumer.acceptResourceFile(file, Files.readAllBytes(file)); - } catch (IOException e) { - throw new RuntimeException(e); - } - }, executor)); - } - } - - return FileVisitResult.CONTINUE; - } - }); - } catch (IOException e) { - throw new RuntimeException(e); - } - - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + public String toString() { + return String.format("jar %s", path); } - @Override - public void close() throws IOException { - FileSystemHandler.close(this.jarFileSystem); - } + private final Path path; } diff --git a/src/main/java/net/fabricmc/tinyremapper/util/PathInputSupplier.java b/src/main/java/net/fabricmc/tinyremapper/util/PathInputSupplier.java new file mode 100644 index 00000000..3ab29e47 --- /dev/null +++ b/src/main/java/net/fabricmc/tinyremapper/util/PathInputSupplier.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016, 2018, Player, asie + * Copyright (c) 2021, FabricMC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package net.fabricmc.tinyremapper.util; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.FileSystem; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.function.Predicate; + +import net.fabricmc.tinyremapper.FileSystemHandler; +import net.fabricmc.tinyremapper.api.io.InputSupplier; + +public final class PathInputSupplier implements InputSupplier { + public PathInputSupplier(Path path) throws IOException { + this.path = path; + } + + @Override + public String getSource() { + return path.toString(); + } + + @Override + public CompletableFuture read(Predicate fileNameFilter, Executor executor, InputConsumer consumer) throws IOException { + return read(path, fileNameFilter, true, executor, consumer); + } + + static CompletableFuture read(Path path, Predicate fileNameFilter, boolean recurseZip, Executor executor, InputConsumer consumer) throws IOException { + List> futures = new ArrayList<>(); + + Files.walkFileTree(path, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String fileName = file.getFileName().toString().toLowerCase(Locale.ENGLISH); + + if (recurseZip && (fileName.endsWith(".zip") || fileName.endsWith(".jar"))) { + futures.add(readZip(file, fileNameFilter, executor, consumer)); + } else if (fileNameFilter.test(fileName)) { + futures.add(CompletableFuture.runAsync(() -> { + byte[] data; + + try { + data = Files.readAllBytes(file); + } catch (IOException e) { + throw new UncheckedIOException("Error reading "+file, e); + } + + consumer.accept(file, data); + }, executor)); + } + + return FileVisitResult.CONTINUE; + } + }); + + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + } + + static CompletableFuture readZip(Path file, Predicate fileNameFilter, Executor executor, InputConsumer consumer) throws IOException { + if (Files.isDirectory(file)) throw new IOException("not a jar/zip file: "+file); + + FileSystem fs = FileSystemHandler.open(file); + Path root = fs.getPath("/"); + + return read(root, fileNameFilter, false, executor, consumer) + .whenComplete((res, exc) -> { + try { + FileSystemHandler.close(fs); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + @Override + public String toString() { + return String.format("path %s", path); + } + + private final Path path; +} diff --git a/src/test/java/net/fabricmc/tinyremapper/TinyRemapperTest.java b/src/test/java/net/fabricmc/tinyremapper/TinyRemapperTest.java index af975992..82ece179 100644 --- a/src/test/java/net/fabricmc/tinyremapper/TinyRemapperTest.java +++ b/src/test/java/net/fabricmc/tinyremapper/TinyRemapperTest.java @@ -26,6 +26,8 @@ import org.junit.jupiter.api.Test; +import net.fabricmc.tinyremapper.api.TrEnvironment; + class TinyRemapperTest { @Test public void analyzeMrjVersion() throws ReflectiveOperationException { @@ -53,27 +55,27 @@ public void analyzeMrjVersion() throws ReflectiveOperationException { input = "/path/to/bin/com/github/logicf/App.class"; result = getMrjVersionFromPath(input, name); - assertEquals(ClassInstance.MRJ_DEFAULT, result); + assertEquals(TrEnvironment.DEFAULT_MRJ_VERSION, result); input = "/path/to/bin/META-INF/versions/16/abc/com/github/logicf/App.class"; result = getMrjVersionFromPath(input, name); - assertEquals(ClassInstance.MRJ_DEFAULT, result); + assertEquals(TrEnvironment.DEFAULT_MRJ_VERSION, result); input = "/path/to/bin/versions/16/com/github/logicf/App.class"; result = getMrjVersionFromPath(input, name); - assertEquals(ClassInstance.MRJ_DEFAULT, result); + assertEquals(TrEnvironment.DEFAULT_MRJ_VERSION, result); input = "/META-INF/versions/9aa/com/github/logicf/App.class"; result = getMrjVersionFromPath(input, name); - assertEquals(ClassInstance.MRJ_DEFAULT, result); + assertEquals(TrEnvironment.DEFAULT_MRJ_VERSION, result); input = "/bin/App.class"; result = getMrjVersionFromPath(input, name); - assertEquals(ClassInstance.MRJ_DEFAULT, result); + assertEquals(TrEnvironment.DEFAULT_MRJ_VERSION, result); input = "App.class"; result = getMrjVersionFromPath(input, name); - assertEquals(ClassInstance.MRJ_DEFAULT, result); + assertEquals(TrEnvironment.DEFAULT_MRJ_VERSION, result); } private static int getMrjVersionFromPath(String file, String name) throws ReflectiveOperationException {