From 782105a048a63c7a8a23745f07d66ae9359b4029 Mon Sep 17 00:00:00 2001 From: Josef Cacek Date: Tue, 7 Jan 2020 08:54:55 +0100 Subject: [PATCH] Fix loading of embedded/inner/annonymous classes --- .../github/kwart/jd/input/ClassFileInput.java | 2 +- .../com/github/kwart/jd/input/DirInput.java | 7 +-- .../github/kwart/jd/loader/FileLoader.java | 42 ++++++++++++--- .../java/com/github/kwart/jd/HelloWorld.java | 23 ++++++++ .../github/kwart/jd/JavaDecompilerTest.java | 52 +++++++++++++------ 5 files changed, 98 insertions(+), 28 deletions(-) diff --git a/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java b/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java index 09540fd..e090d76 100644 --- a/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java +++ b/jd-lib/src/main/java/com/github/kwart/jd/input/ClassFileInput.java @@ -53,7 +53,7 @@ public void decompile(JavaDecompiler javaDecompiler, JDOutput jdOutput) { final String name = file.getName(); LOGGER.debug("Decompiling single class file {}", name); String nameWithoutClassSfx = IOUtils.isClassFile(name) ? IOUtils.cutClassSuffix(name) : name; - jdOutput.processClass(nameWithoutClassSfx, javaDecompiler.decompileClass(FileLoader.INSTANCE, file.getPath())); + jdOutput.processClass(nameWithoutClassSfx, javaDecompiler.decompileClass(new FileLoader(), file.getPath())); jdOutput.commit(); } } diff --git a/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java b/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java index 709a54f..d072959 100644 --- a/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java +++ b/jd-lib/src/main/java/com/github/kwart/jd/input/DirInput.java @@ -36,13 +36,14 @@ public class DirInput extends AbstractFileJDInput { private static final Logger LOGGER = LoggerFactory.getLogger(DirInput.class); + private final FileLoader fileLoader; public DirInput(String path) { super(path); if (!file.isDirectory()) { throw new IllegalArgumentException("Path doesn't denote a directory."); } - + this.fileLoader = new FileLoader(path); } @Override @@ -76,8 +77,8 @@ private void processFile(JavaDecompiler javaDecompiler, JDOutput jdOutput, Strin return; } LOGGER.debug("Decompiling {}", nextFile); - jdOutput.processClass(IOUtils.cutClassSuffix(nameWithPath), - javaDecompiler.decompileClass(FileLoader.INSTANCE, nextFile.getAbsolutePath())); + String internalName = IOUtils.cutClassSuffix(nameWithPath); + jdOutput.processClass(internalName, javaDecompiler.decompileClass(fileLoader, internalName)); } else if (!javaDecompiler.getOptions().isSkipResources()) { LOGGER.debug("Processing resource file {}", nextFile); FileInputStream fis = null; diff --git a/jd-lib/src/main/java/com/github/kwart/jd/loader/FileLoader.java b/jd-lib/src/main/java/com/github/kwart/jd/loader/FileLoader.java index ce3a3d5..e045380 100644 --- a/jd-lib/src/main/java/com/github/kwart/jd/loader/FileLoader.java +++ b/jd-lib/src/main/java/com/github/kwart/jd/loader/FileLoader.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import org.jd.core.v1.api.loader.Loader; @@ -12,18 +13,20 @@ */ public final class FileLoader implements Loader { - /** - * Singleton instance. - */ - public static final FileLoader INSTANCE = new FileLoader(); + private final String basePath; - private FileLoader() { + public FileLoader() { + this(null); + } + + public FileLoader(String basePath) { + this.basePath = basePath; } @Override public byte[] load(String internalName) throws LoaderException { try { - return Files.readAllBytes(Paths.get(internalName)); + return Files.readAllBytes(fixPath(internalName)); } catch (IOException e) { throw new LoaderException(e); } @@ -31,6 +34,31 @@ public byte[] load(String internalName) throws LoaderException { @Override public boolean canLoad(String internalName) { - return Files.isReadable(Paths.get(internalName)); + Path fixedPath = fixPath(internalName); + return fixedPath != null; + } + + public Path fixPath(String internalName) { + Path path = Paths.get(internalName); + if (Files.isReadable(path)) { + return path; + } + if (basePath != null) { + path = Paths.get(basePath, internalName); + if (Files.isReadable(path)) { + return path; + } + } + path = Paths.get(internalName + ".class"); + if (Files.isReadable(path)) { + return path; + } + if (basePath != null) { + path = Paths.get(basePath, internalName + ".class"); + if (Files.isReadable(path)) { + return path; + } + } + return null; } } diff --git a/jd-lib/src/test/java/com/github/kwart/jd/HelloWorld.java b/jd-lib/src/test/java/com/github/kwart/jd/HelloWorld.java index 31a38c9..d898294 100644 --- a/jd-lib/src/test/java/com/github/kwart/jd/HelloWorld.java +++ b/jd-lib/src/test/java/com/github/kwart/jd/HelloWorld.java @@ -5,11 +5,34 @@ */ public final class HelloWorld { + private final PrivateClass priv; + private HelloWorld() { System.out.println("ctor"); + priv = new PrivateClass(); + priv.test(); } public static void main(String[] args) { System.out.println("xxx"); + StaticClass.test(); + } + + /** + * Static test class. + */ + public static class StaticClass { + public static String test() { + return "static"; + } + } + + /** + * Private test class. + */ + private class PrivateClass { + public String test() { + return "private"; + } } } diff --git a/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java b/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java index 58193fc..7a68cd3 100644 --- a/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java +++ b/jd-lib/src/test/java/com/github/kwart/jd/JavaDecompilerTest.java @@ -1,29 +1,41 @@ package com.github.kwart.jd; -import static com.github.kwart.jd.JavaDecompilerConstants.CLASS_SUFFIX; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import org.jd.core.v1.api.loader.Loader; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + import org.jd.core.v1.api.loader.LoaderException; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TemporaryFolder; -import com.github.kwart.jd.loader.ByteArrayLoader; +import com.github.kwart.jd.input.DirInput; +import com.github.kwart.jd.input.JDInput; import com.github.kwart.jd.options.DecompilerOptions; +import com.github.kwart.jd.output.DirOutput; +import com.github.kwart.jd.output.JDOutput; /** * Basic test for {@link JavaDecompiler} class. */ public class JavaDecompilerTest { - @Test - public void test() throws LoaderException { - - Class clazz = HelloWorld.class; - String internalName = clazz.getName(); + /** + * Temporary folder. + */ + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); - Loader loader = new ByteArrayLoader(clazz.getResourceAsStream("/" + internalName.replace('.', '/') + CLASS_SUFFIX), - internalName); - String decompiled = new JavaDecompiler(new DecompilerOptions() { + @Test + public void test() throws LoaderException, IOException { + JDInput input = new DirInput("target/test-classes"); + File tmpFolder = temporaryFolder.getRoot(); + JDOutput output = new DirOutput(tmpFolder.getAbsoluteFile()); + JavaDecompiler decompiler = new JavaDecompiler(new DecompilerOptions() { @Override public boolean isSkipResources() { @@ -39,12 +51,18 @@ public boolean isEscapeUnicodeCharacters() { public boolean isDisplayLineNumbers() { return false; } - }).decompileClass(loader, internalName); - System.out.println(decompiled); - assertTrue(decompiled.contains("public final class HelloWorld {")); - assertTrue(decompiled.contains("public static void main(String[] args)")); - assertTrue(decompiled.contains("System.out.println(\"xxx\");")); - assertTrue(decompiled.contains("private HelloWorld() {")); + }); + input.decompile(decompiler, output); + File decompiledFile = new File(tmpFolder, "com/github/kwart/jd/HelloWorld.java"); + assertTrue(decompiledFile.isFile()); + String decompiled = new String(Files.readAllBytes(decompiledFile.toPath())); + assertThat(decompiled, containsString("public final class HelloWorld {")); + assertThat(decompiled, containsString("public static void main(String[] args)")); + assertThat(decompiled, containsString("System.out.println(\"xxx\");")); + assertThat(decompiled, containsString("private HelloWorld() {")); + + assertThat(decompiled, containsString("public static class StaticClass {")); + assertThat(decompiled, containsString("private class PrivateClass {")); } }