diff options
28 files changed, 482 insertions, 672 deletions
diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 29307e5..63f60ba 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -630,7 +630,7 @@ public class ClassWriter { if (isEnum && init) actualParams -= 2; if (actualParams != descriptor.params.size()) { String message = "Inconsistent generic signature in method " + mt.getName() + " " + mt.getDescriptor(); - DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.WARNING); + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); descriptor = null; } } diff --git a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java index 74c86d0..6faad08 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/ClassesProcessor.java @@ -113,7 +113,7 @@ public class ClassesProcessor { else { if (!InterpreterUtil.equalObjectArrays(arrold, arr)) { DecompilerContext.getLogger() - .writeMessage("Inconsistent inner class entries for " + innername + "!", IFernflowerLogger.WARNING); + .writeMessage("Inconsistent inner class entries for " + innername + "!", IFernflowerLogger.Severity.WARN); } } @@ -178,7 +178,8 @@ public class ClassesProcessor { ClassNode nestednode = mapRootClasses.get(nestedClass); if (nestednode == null) { - DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", IFernflowerLogger.WARNING); + DecompilerContext.getLogger().writeMessage("Nested class " + nestedClass + " missing!", + IFernflowerLogger.Severity.WARN); continue; } @@ -204,7 +205,7 @@ public class ClassesProcessor { if (interfaces.length > 0) { if (interfaces.length > 1) { DecompilerContext.getLogger() - .writeMessage("Inconsistent anonymous class definition: " + cl.qualifiedName, IFernflowerLogger.WARNING); + .writeMessage("Inconsistent anonymous class definition: " + cl.qualifiedName, IFernflowerLogger.Severity.WARN); } nestednode.anonimousClassType = new VarType(cl.getInterface(0), true); } diff --git a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java index 54c426b..b600250 100644 --- a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java +++ b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java @@ -132,12 +132,12 @@ public class DecompilerContext { public static void setLogger(IFernflowerLogger logger) { if (logger != null) { - String severity = (String)getProperty(IFernflowerPreferences.LOG_LEVEL); - if (severity != null) { - Integer iSeverity = IFernflowerLogger.mapLogLevel.get(severity.toUpperCase(Locale.US)); - if (iSeverity != null) { - logger.setSeverity(iSeverity); + String level = (String)getProperty(IFernflowerPreferences.LOG_LEVEL); + if (level != null) { + try { + logger.setSeverity(IFernflowerLogger.Severity.valueOf(level.toUpperCase(Locale.US))); } + catch (IllegalArgumentException ignore) { } } } getCurrentContext().logger = logger; diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java index 34f9196..62b8ee4 100644 --- a/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -18,7 +18,8 @@ package org.jetbrains.java.decompiler.main; import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; -import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; import org.jetbrains.java.decompiler.modules.renamer.IdentifierConverter; import org.jetbrains.java.decompiler.struct.IDecompiledData; @@ -28,16 +29,16 @@ import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; import java.util.Map; - public class Fernflower implements IDecompiledData { private StructContext structContext; private ClassesProcessor classesProcessor; - public Fernflower(IBytecodeProvider provider, IDecompilatSaver saver, Map<String, Object> propertiesCustom) { + public Fernflower(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) { structContext = new StructContext(saver, this, new LazyLoader(provider)); - DecompilerContext.initContext(propertiesCustom); + DecompilerContext.initContext(options); DecompilerContext.setCounterContainer(new CounterContainer()); + DecompilerContext.setLogger(logger); } public void decompileContext() { @@ -57,6 +58,11 @@ public class Fernflower implements IDecompiledData { DecompilerContext.setCurrentContext(null); } + public StructContext getStructContext() { + return structContext; + } + + @Override public String getClassEntryName(StructClass cl, String entryName) { ClassNode node = classesProcessor.getMapRootClasses().get(cl.qualifiedName); if (node.type != ClassNode.CLASS_ROOT) { @@ -73,10 +79,7 @@ public class Fernflower implements IDecompiledData { } } - public StructContext getStructContext() { - return structContext; - } - + @Override public String getClassContent(StructClass cl) { try { StringBuilder buffer = new StringBuilder(); diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java index 212a8bd..07b8578 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/IdeDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/BaseDecompiler.java @@ -15,28 +15,21 @@ */ package org.jetbrains.java.decompiler.main.decompiler; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.Fernflower; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; -import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.extern.IResultSaver; import java.io.File; import java.io.IOException; -import java.util.HashMap; +import java.util.Map; +public class BaseDecompiler { -public class IdeDecompiler { + private final Fernflower fernflower; - private Fernflower fernflower; - - public IdeDecompiler(IBytecodeProvider provider, - IDecompilatSaver saver, IFernflowerLogger logger, - HashMap<String, Object> propertiesCustom) { - - fernflower = new Fernflower(provider, saver, propertiesCustom); - - DecompilerContext.setLogger(logger); + public BaseDecompiler(IBytecodeProvider provider, IResultSaver saver, Map<String, Object> options, IFernflowerLogger logger) { + fernflower = new Fernflower(provider, saver, options, logger); } public void addSpace(File file, boolean isOwn) throws IOException { diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java index d499324..a486d9f 100644 --- a/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java +++ b/src/org/jetbrains/java/decompiler/main/decompiler/ConsoleDecompiler.java @@ -17,10 +17,9 @@ package org.jetbrains.java.decompiler.main.decompiler; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.Fernflower; -import org.jetbrains.java.decompiler.main.decompiler.helper.PrintStreamLogger; import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider; -import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.io.*; @@ -31,108 +30,97 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -@SuppressWarnings({"UseOfSystemOutOrSystemErr", "CallToPrintStackTrace"}) -public class ConsoleDecompiler implements IBytecodeProvider, IDecompilatSaver { +public class ConsoleDecompiler implements IBytecodeProvider, IResultSaver { - private File root; + @SuppressWarnings("UseOfSystemOutOrSystemErr") + public static void main(String[] args) { + if (args.length < 2) { + System.out.println( + "Usage: java -jar fernflower.jar [-<option>=<value>]* [<source>]+ <destination>\n" + + "Example: java -jar fernflower.jar -dgs=true c:\\my\\source\\ c:\\my.jar d:\\decompiled\\"); + return; + } - private Fernflower fernflower; + Map<String, Object> mapOptions = new HashMap<String, Object>(); + List<String> lstSources = new ArrayList<String>(); + List<String> lstLibraries = new ArrayList<String>(); - private HashMap<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>(); + boolean isOption = true; + for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination + String arg = args[i]; - private HashMap<String, HashSet<String>> mapArchiveEntries = new HashMap<String, HashSet<String>>(); + if (isOption && arg.startsWith("-") && + arg.length() > 5 && arg.charAt(4) == '=') { + String value = arg.substring(5).toUpperCase(Locale.US); + if ("TRUE".equals(value)) { + value = "1"; + } + else if ("FALSE".equals(value)) { + value = "0"; + } - public ConsoleDecompiler() { - this(null); - } + mapOptions.put(arg.substring(1, 4), value); + } + else { + isOption = false; - public ConsoleDecompiler(HashMap<String, Object> propertiesCustom) { - this(new PrintStreamLogger(IFernflowerLogger.WARNING, System.out), propertiesCustom); - } + if (arg.startsWith("-e=")) { + lstLibraries.add(arg.substring(3)); + } + else { + lstSources.add(arg); + } + } + } - protected ConsoleDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) { - fernflower = new Fernflower(this, this, propertiesCustom); - DecompilerContext.setLogger(logger); - } + if (lstSources.isEmpty()) { + System.out.println("error: no sources given"); + return; + } - public static void main(String[] args) { + File destination = new File(args[args.length - 1]); + if (!destination.isDirectory()) { + System.out.println("error: destination '" + destination + "' is not a directory"); + return; + } - try { + PrintStreamLogger logger = new PrintStreamLogger(System.out); + ConsoleDecompiler decompiler = new ConsoleDecompiler(destination, mapOptions, logger); - if (args != null && args.length > 1) { - - HashMap<String, Object> mapOptions = new HashMap<String, Object>(); - - List<String> lstSources = new ArrayList<String>(); - List<String> lstLibraries = new ArrayList<String>(); - - boolean isOption = true; - for (int i = 0; i < args.length - 1; ++i) { // last parameter - destination - String arg = args[i]; - - if (isOption && arg.startsWith("-") && - arg.length() > 5 && arg.charAt(4) == '=') { - String value = arg.substring(5).toUpperCase(Locale.US); - if ("TRUE".equals(value)) { - value = "1"; - } - else if ("FALSE".equals(value)) { - value = "0"; - } - - mapOptions.put(arg.substring(1, 4), value); - } - else { - isOption = false; - - if (arg.startsWith("-e=")) { - lstLibraries.add(arg.substring(3)); - } - else { - lstSources.add(arg); - } - } - } + for (String source : lstSources) { + decompiler.addSpace(new File(source), true); + } + for (String library : lstLibraries) { + decompiler.addSpace(new File(library), false); + } - if (lstSources.isEmpty()) { - printHelp(); - } - else { - ConsoleDecompiler decompiler = new ConsoleDecompiler( - new PrintStreamLogger(IFernflowerLogger.INFO, System.out), - mapOptions); + decompiler.decompileContext(); + } - for (String source : lstSources) { - decompiler.addSpace(new File(source), true); - } + // ******************************************************************* + // Implementation + // ******************************************************************* - for (String library : lstLibraries) { - decompiler.addSpace(new File(library), false); - } + private final File root; + private final Fernflower fernflower; + private Map<String, ZipOutputStream> mapArchiveStreams = new HashMap<String, ZipOutputStream>(); + private Map<String, Set<String>> mapArchiveEntries = new HashMap<String, Set<String>>(); - decompiler.decompileContext(new File(args[args.length - 1])); - } - } - else { - printHelp(); - } - } - catch (Exception ex) { - ex.printStackTrace(); - } + @SuppressWarnings("UseOfSystemOutOrSystemErr") + public ConsoleDecompiler(File destination, Map<String, Object> options) { + this(destination, options, new PrintStreamLogger(System.out)); } - private static void printHelp() { - System.out.println("Usage: java ConsoleDecompiler ( -<option>=<value>)* (<source>)+ <destination>"); - System.out.println("Example: java ConsoleDecompiler -dgs=true c:\\mysource\\ c:\\my.jar d:\\decompiled\\"); + protected ConsoleDecompiler(File destination, Map<String, Object> options, IFernflowerLogger logger) { + root = destination; + fernflower = new Fernflower(this, this, options, logger); } - public void addSpace(File file, boolean isOwn) throws IOException { + public void addSpace(File file, boolean isOwn) { fernflower.getStructContext().addSpace(file, isOwn); } - public void decompileContext(File root) { - this.root = root; + public void decompileContext() { try { fernflower.decompileContext(); } @@ -145,183 +133,167 @@ public class ConsoleDecompiler implements IBytecodeProvider, IDecompilatSaver { // Interface IBytecodeProvider // ******************************************************************* - public InputStream getBytecodeStream(String externPath, String internPath) { - - try { - File file = new File(externPath); - - if (internPath == null) { - return new FileInputStream(file); - } - else { // archive file - ZipFile archive = new ZipFile(file); - - Enumeration<? extends ZipEntry> en = archive.entries(); - while (en.hasMoreElements()) { - ZipEntry entr = en.nextElement(); - - if (entr.getName().equals(internPath)) { - return archive.getInputStream(entr); - } + @Override + public byte[] getBytecode(String externalPath, String internalPath) throws IOException { + File file = new File(externalPath); + if (internalPath == null) { + return InterpreterUtil.getBytes(file); + } + else { + ZipFile archive = new ZipFile(file); + try { + ZipEntry entry = archive.getEntry(internalPath); + if (entry == null) { + throw new IOException("Entry not found: " + internalPath); } + return InterpreterUtil.getBytes(archive, entry); + } + finally { + archive.close(); } } - catch (IOException ex) { - ex.printStackTrace(); - } - - return null; } // ******************************************************************* - // Interface IDecompilatSaver + // Interface IResultSaver // ******************************************************************* private String getAbsolutePath(String path) { return new File(root, path).getAbsolutePath(); } - private boolean addEntryName(String filename, String entry) { - HashSet<String> set = mapArchiveEntries.get(filename); - if (set == null) { - mapArchiveEntries.put(filename, set = new HashSet<String>()); + @Override + public void saveFolder(String path) { + File dir = new File(getAbsolutePath(path)); + if (!(dir.mkdirs() || dir.isDirectory())) { + throw new RuntimeException("Cannot create directory " + dir); } - - return set.add(entry); } - public void copyEntry(String source, String destpath, String archivename, String entryName) { - + @Override + public void copyFile(String source, String path, String entryName) { try { - String filename = new File(getAbsolutePath(destpath), archivename).getAbsolutePath(); - - if (!addEntryName(filename, entryName)) { - DecompilerContext.getLogger().writeMessage("Zip entry already exists: " + - destpath + "," + archivename + "," + entryName, IFernflowerLogger.WARNING); - return; - } - - ZipFile srcarchive = new ZipFile(new File(source)); - - Enumeration<? extends ZipEntry> en = srcarchive.entries(); - while (en.hasMoreElements()) { - ZipEntry entr = en.nextElement(); - - if (entr.getName().equals(entryName)) { - InputStream in = srcarchive.getInputStream(entr); - - ZipOutputStream out = mapArchiveStreams.get(filename); - out.putNextEntry(new ZipEntry(entryName)); - - InterpreterUtil.copyStream(in, out); - in.close(); - } - } - - srcarchive.close(); + InterpreterUtil.copyFile(new File(source), new File(getAbsolutePath(path), entryName)); } catch (IOException ex) { - DecompilerContext.getLogger() - .writeMessage("Error copying zip file entry: " + source + "," + destpath + "," + archivename + "," + entryName, - IFernflowerLogger.WARNING); - ex.printStackTrace(); + DecompilerContext.getLogger().writeMessage("Cannot copy " + source + " to " + entryName, ex); } } - public void copyFile(String source, String destpath, String destfilename) { + @Override + public void saveClassFile(String path, String qualifiedName, String entryName, String content) { + File file = new File(getAbsolutePath(path), entryName); try { - InterpreterUtil.copyFile(new File(source), new File(destfilename)); + Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF8"); + try { + out.write(content); + } + finally { + out.close(); + } } catch (IOException ex) { - ex.printStackTrace(); + DecompilerContext.getLogger().writeMessage("Cannot write class file " + file, ex); } } - public void saveFile(String path, String filename, String content) { + @Override + public void createArchive(String path, String archiveName, Manifest manifest) { + File file = new File(getAbsolutePath(path), archiveName); try { - BufferedWriter out = - new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(getAbsolutePath(path), filename)), "UTF8")); - out.write(content); - out.flush(); - out.close(); + if (!(file.createNewFile() || file.isFile())) { + throw new IOException("Cannot create file " + file); + } + + FileOutputStream fileStream = new FileOutputStream(file); + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + ZipOutputStream zipStream = manifest != null ? new JarOutputStream(fileStream, manifest) : new ZipOutputStream(fileStream); + mapArchiveStreams.put(file.getPath(), zipStream); } catch (IOException ex) { - ex.printStackTrace(); + DecompilerContext.getLogger().writeMessage("Cannot create archive " + file, ex); } } - public void createArchive(String path, String archivename, Manifest manifest) { + @Override + public void saveDirEntry(String path, String archiveName, String entryName) { + saveClassEntry(path, archiveName, null, entryName, null); + } - try { - File file = new File(getAbsolutePath(path), archivename); - file.createNewFile(); + @Override + public void copyEntry(String source, String path, String archiveName, String entryName) { + String file = new File(getAbsolutePath(path), archiveName).getPath(); + + if (!checkEntry(entryName, file)) { + return; + } - ZipOutputStream out; - if (manifest != null) { // jar - out = new JarOutputStream(new FileOutputStream(file), manifest); + try { + ZipFile srcArchive = new ZipFile(new File(source)); + try { + ZipEntry entry = srcArchive.getEntry(entryName); + if (entry != null) { + InputStream in = srcArchive.getInputStream(entry); + ZipOutputStream out = mapArchiveStreams.get(file); + out.putNextEntry(new ZipEntry(entryName)); + InterpreterUtil.copyStream(in, out); + in.close(); + } } - else { - out = new ZipOutputStream(new FileOutputStream(file)); + finally { + srcArchive.close(); } - mapArchiveStreams.put(file.getAbsolutePath(), out); } catch (IOException ex) { - ex.printStackTrace(); + String message = "Cannot copy entry " + entryName + " from " + source + " to " + file; + DecompilerContext.getLogger().writeMessage(message, ex); } } - public void saveClassEntry(String path, String archivename, - String qualifiedName, String entryName, String content) { - saveEntry(path, archivename, entryName, content); - } + @Override + public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { + String file = new File(getAbsolutePath(path), archiveName).getPath(); - public void saveClassFile(String path, String qualifiedName, String entryName, String content) { - saveFile(path, entryName, content); - } - - public void saveEntry(String path, String archivename, String entryName, - String content) { + if (!checkEntry(entryName, file)) { + return; + } try { - String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath(); - - if (!addEntryName(filename, entryName)) { - DecompilerContext.getLogger().writeMessage("Zip entry already exists: " + - path + "," + archivename + "," + entryName, IFernflowerLogger.WARNING); - return; - } - - ZipOutputStream out = mapArchiveStreams.get(filename); + ZipOutputStream out = mapArchiveStreams.get(file); out.putNextEntry(new ZipEntry(entryName)); - if (content != null) { - BufferedWriter outwriter = new BufferedWriter(new OutputStreamWriter(out, "UTF8")); - outwriter.write(content); - outwriter.flush(); + out.write(content.getBytes("UTF-8")); } } catch (IOException ex) { - ex.printStackTrace(); + String message = "Cannot write entry " + entryName + " to " + file; + DecompilerContext.getLogger().writeMessage(message, ex); } } - public void saveFolder(String path) { - File f = new File(getAbsolutePath(path)); - f.mkdirs(); - } + private boolean checkEntry(String entryName, String file) { + Set<String> set = mapArchiveEntries.get(file); + if (set == null) { + mapArchiveEntries.put(file, set = new HashSet<String>()); + } + boolean added = set.add(entryName); + if (!added) { + String message = "Zip entry " + entryName + " already exists in " + file; + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN); + } + return added; + } - public void closeArchive(String path, String archivename) { + @Override + public void closeArchive(String path, String archiveName) { + String file = new File(getAbsolutePath(path), archiveName).getPath(); try { - String filename = new File(getAbsolutePath(path), archivename).getAbsolutePath(); - - mapArchiveEntries.remove(filename); - OutputStream out = mapArchiveStreams.remove(filename); - out.flush(); - out.close(); + mapArchiveEntries.remove(file); + mapArchiveStreams.remove(file).close(); } catch (IOException ex) { - ex.printStackTrace(); + DecompilerContext.getLogger().writeMessage("Cannot close " + file, IFernflowerLogger.Severity.WARN); } } } diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java b/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java new file mode 100644 index 0000000..7bbcc19 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/main/decompiler/PrintStreamLogger.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.java.decompiler.main.decompiler; + +import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +import java.io.PrintStream; + +public class PrintStreamLogger extends IFernflowerLogger { + + private final PrintStream stream; + private int indent; + + public PrintStreamLogger(PrintStream printStream) { + stream = printStream; + indent = 0; + } + + @Override + public void writeMessage(String message, Severity severity) { + if (accepts(severity)) { + stream.println(InterpreterUtil.getIndentString(indent) + severity.name() + ": " + message); + } + } + + @Override + public void writeMessage(String message, Throwable t) { + writeMessage(message, Severity.ERROR); + if (accepts(Severity.ERROR)) { + t.printStackTrace(stream); + } + } + + @Override + public void startClass(String className) { + writeMessage("Processing class " + className + " ...", Severity.INFO); + ++indent; + } + + @Override + public void endClass() { + --indent; + writeMessage("... proceeded.", Severity.INFO); + } + + @Override + public void startWriteClass(String className) { + writeMessage("Writing class " + className + " ...", Severity.INFO); + ++indent; + } + + @Override + public void endWriteClass() { + --indent; + writeMessage("... written.", Severity.INFO); + } + + @Override + public void startMethod(String methodName) { + writeMessage("Processing method " + methodName + " ...", Severity.INFO); + } + + public void endMethod() { + writeMessage("... proceeded.", Severity.INFO); + } +} diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java b/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java deleted file mode 100644 index 06e4c6f..0000000 --- a/src/org/jetbrains/java/decompiler/main/decompiler/WebDecompiler.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.main.decompiler; - -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; - -import java.io.File; -import java.util.HashMap; -import java.util.HashSet; - - -public class WebDecompiler extends ConsoleDecompiler { - - private HashMap<String, File> mapInputFilenames = new HashMap<String, File>(); - - private HashSet<String> setClassFiles = new HashSet<String>(); - - private File root; - - public WebDecompiler(IFernflowerLogger logger, HashMap<String, Object> propertiesCustom) { - super(logger, propertiesCustom); - } - - @Override - public void decompileContext(File root) { - this.root = root; - super.decompileContext(root); - } - - @Override - public void copyFile(String source, String destpath, String destfilename) { - super.copyFile(source, destpath, destfilename); - mapInputFilenames.put(destfilename, new File(getAbsolutePath(destpath), destfilename)); - } - - @Override - public void saveFile(String path, String filename, String content) { - super.saveFile(path, filename, content); - - mapInputFilenames.put(setClassFiles.contains(filename) ? - filename.substring(0, filename.lastIndexOf(".java")) + ".class" : - filename, new File(getAbsolutePath(path), filename)); - } - - @Override - public void saveClassFile(String path, String qualifiedName, String entryName, String content) { - setClassFiles.add(entryName); - saveFile(path, entryName, content); - } - - @Override - public void closeArchive(String path, String archivename) { - super.closeArchive(path, archivename); - mapInputFilenames.put(archivename, new File(getAbsolutePath(path), archivename)); - } - - private String getAbsolutePath(String path) { - return new File(root, path).getAbsolutePath(); - } - - public HashMap<String, File> getMapInputFilenames() { - return mapInputFilenames; - } -} - diff --git a/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java b/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java deleted file mode 100644 index 3a6c7e8..0000000 --- a/src/org/jetbrains/java/decompiler/main/decompiler/helper/PrintStreamLogger.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2000-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.java.decompiler.main.decompiler.helper; - -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; -import org.jetbrains.java.decompiler.util.InterpreterUtil; - -import java.io.PrintStream; - -public class PrintStreamLogger implements IFernflowerLogger { - - private int severity; - - private int indent; - - private PrintStream stream; - - public PrintStreamLogger(int severity, PrintStream stream) { - this.severity = severity; - this.indent = 0; - this.stream = stream; - } - - - public void writeMessage(String message, int severity) { - if (severity >= this.severity) { - stream.println(InterpreterUtil.getIndentString(indent) + names[severity] + ": " + message); - } - } - - public void writeMessage(String message, Throwable t) { - t.printStackTrace(stream); - writeMessage(message, ERROR); - } - - public void startClass(String classname) { - stream.println(InterpreterUtil.getIndentString(indent++) + "Processing class " + classname + " ..."); - } - - public void endClass() { - stream.println(InterpreterUtil.getIndentString(--indent) + "... proceeded."); - } - - public void startWriteClass(String classname) { - stream.println(InterpreterUtil.getIndentString(indent++) + "Writing class " + classname + " ..."); - } - - public void endWriteClass() { - stream.println(InterpreterUtil.getIndentString(--indent) + "... written."); - } - - public void startMethod(String method) { - if (severity <= INFO) { - stream.println(InterpreterUtil.getIndentString(indent) + "Processing method " + method + " ..."); - } - } - - public void endMethod() { - if (severity <= INFO) { - stream.println(InterpreterUtil.getIndentString(indent) + "... proceeded."); - } - } - - public int getSeverity() { - return severity; - } - - public void setSeverity(int severity) { - this.severity = severity; - } -} diff --git a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java index 8fe0b31..adf3270 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IBytecodeProvider.java @@ -15,9 +15,8 @@ */ package org.jetbrains.java.decompiler.main.extern; -import java.io.InputStream; +import java.io.IOException; public interface IBytecodeProvider { - - InputStream getBytecodeStream(String externPath, String internPath); + byte[] getBytecode(String externalPath, String internalPath) throws IOException; } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java index d9c8f74..65b2c6d 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerLogger.java @@ -15,43 +15,35 @@ */ package org.jetbrains.java.decompiler.main.extern; -import java.util.HashMap; +public abstract class IFernflowerLogger { -public interface IFernflowerLogger { + public enum Severity { + TRACE, INFO, WARN, ERROR + } - int TRACE = 1; - int INFO = 2; - int WARNING = 3; - int ERROR = 4; - int IMMEDIATE = 5; + private Severity severity = Severity.INFO; - HashMap<String, Integer> mapLogLevel = new HashMap<String, Integer>() {{ - put("TRACE", 1); - put("INFO", 2); - put("WARN", 3); - put("ERROR", 4); - put("IMME", 5); - }}; + public boolean accepts(Severity severity) { + return severity.ordinal() >= this.severity.ordinal(); + } - String[] names = new String[]{""/*DUMMY ENTRY*/, "TRACE", "INFO", "WARNING", "ERROR", ""/*IMMEDIATE*/}; + public void setSeverity(Severity severity) { + this.severity = severity; + } - void writeMessage(String message, int severity); + public abstract void writeMessage(String message, Severity severity); - void writeMessage(String message, Throwable t); + public abstract void writeMessage(String message, Throwable t); - void startClass(String classname); + public void startClass(String className) { } - void endClass(); + public void endClass() { } - void startWriteClass(String classname); + public void startWriteClass(String className) { } - void endWriteClass(); + public void endWriteClass() { } - void startMethod(String method); + public void startMethod(String methodName) { } - void endMethod(); - - int getSeverity(); - - void setSeverity(int severity); + public void endMethod() { } } diff --git a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java index c942bad..69dd218 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IFernflowerPreferences.java @@ -77,6 +77,7 @@ public interface IFernflowerPreferences { put(IDEA_NOT_NULL_ANNOTATION, "1"); put(LAMBDA_TO_ANONYMOUS_CLASS, "0"); + put(LOG_LEVEL, IFernflowerLogger.Severity.INFO.name()); put(MAX_PROCESSING_METHOD, "0"); put(RENAME_ENTITIES, "0"); put(NEW_LINE_SEPARATOR, (InterpreterUtil.IS_WINDOWS ? "0" : "1")); diff --git a/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java index 9cbac0c..1e6a7d4 100644 --- a/src/org/jetbrains/java/decompiler/main/extern/IDecompilatSaver.java +++ b/src/org/jetbrains/java/decompiler/main/extern/IResultSaver.java @@ -17,23 +17,20 @@ package org.jetbrains.java.decompiler.main.extern; import java.util.jar.Manifest; -public interface IDecompilatSaver { - - void copyFile(String source, String destpath, String destfilename); - +public interface IResultSaver { void saveFolder(String path); - void saveClassFile(String path, String qualifiedName, String entryName, String content); + void copyFile(String source, String path, String entryName); - void saveFile(String path, String filename, String content); + void saveClassFile(String path, String qualifiedName, String entryName, String content); - void createArchive(String path, String archivename, Manifest manifest); + void createArchive(String path, String archiveName, Manifest manifest); - void saveClassEntry(String path, String archivename, String qualifiedName, String entryName, String content); + void saveDirEntry(String path, String archiveName, String entryName); - void saveEntry(String path, String archivename, String entryName, String content); + void copyEntry(String source, String path, String archiveName, String entry); - void copyEntry(String source, String destpath, String archivename, String entry); + void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content); - void closeArchive(String path, String archivename); + void closeArchive(String path, String archiveName); } diff --git a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java index ae251b9..4a32179 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java @@ -106,7 +106,7 @@ public class ClassWrapper { if (System.currentTimeMillis() >= stopAt) { String message = "Processing time limit exceeded for method " + mt.getName() + ", execution interrupted."; - DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.ERROR); + DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.ERROR); killThread(mtthread); isError = true; break; diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java index 426f143..2f98cf8 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorThread.java @@ -130,7 +130,7 @@ public class MethodProcessorThread implements Runnable { //System.out.println(graph.toString()); if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) { - DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.WARNING); + DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN); } RootStatement root = DomHelper.parseGraph(graph); diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 684864d..2e83946 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -75,7 +75,8 @@ public class NestedClassProcessor { child.simpleName = "SyntheticClass_" + (++synthetics); } else { - DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", IFernflowerLogger.WARNING); + DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", + IFernflowerLogger.Severity.WARN); child.simpleName = "NamelessClass_" + (++nameless); } } @@ -221,11 +222,11 @@ public class NestedClassProcessor { if (!hasEnclosing) { if (child.type == ClassNode.CLASS_ANONYMOUS) { DecompilerContext.getLogger() - .writeMessage("Unreferenced anonymous class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.WARNING); + .writeMessage("Unreferenced anonymous class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.Severity.WARN); } else if (child.type == ClassNode.CLASS_LOCAL) { DecompilerContext.getLogger() - .writeMessage("Unreferenced local class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.WARNING); + .writeMessage("Unreferenced local class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.Severity.WARN); } } } @@ -275,7 +276,7 @@ public class NestedClassProcessor { HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper); if (mask.isEmpty()) { DecompilerContext.getLogger() - .writeMessage("Nested class " + nd.classStruct.qualifiedName + " has no constructor!", IFernflowerLogger.WARNING); + .writeMessage("Nested class " + nd.classStruct.qualifiedName + " has no constructor!", IFernflowerLogger.Severity.WARN); } else { mapVarMasks.put(nd.classStruct.qualifiedName, mask); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java index 422b93b..3bb4584 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/DomHelper.java @@ -213,7 +213,7 @@ public class DomHelper { RootStatement root = graphToStatement(graph); if (!processStatement(root, new HashMap<Integer, Set<Integer>>())) { - DecompilerContext.getLogger().writeMessage("parsing failure!", IFernflowerLogger.ERROR); + DecompilerContext.getLogger().writeMessage("parsing failure!", IFernflowerLogger.Severity.ERROR); // try { // DotExporter.toDotFile(root.getFirst().getStats().get(13), new File("c:\\Temp\\stat1.dot")); @@ -348,13 +348,14 @@ public class DomHelper { // take care of irreducible control flow graphs if (IrreducibleCFGDeobfuscator.isStatementIrreducible(general)) { if (!IrreducibleCFGDeobfuscator.splitIrreducibleNode(general)) { - DecompilerContext.getLogger().writeMessage("Irreducible statement cannot be decomposed!", IFernflowerLogger.ERROR); + DecompilerContext.getLogger().writeMessage("Irreducible statement cannot be decomposed!", IFernflowerLogger.Severity.ERROR); break; } } else { if (mapstage == 2 || mapRefreshed) { // last chance lost - DecompilerContext.getLogger().writeMessage("Statement cannot be decomposed although reducible!", IFernflowerLogger.ERROR); + DecompilerContext.getLogger().writeMessage("Statement cannot be decomposed although reducible!", + IFernflowerLogger.Severity.ERROR); } break; } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index 024b806..cbdb3b7 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -463,7 +463,7 @@ public class Statement { } if (res.size() != stats.size()) { - DecompilerContext.getLogger().writeMessage("computing post reverse post order failed!", IFernflowerLogger.ERROR); + DecompilerContext.getLogger().writeMessage("computing post reverse post order failed!", IFernflowerLogger.Severity.ERROR); throw new RuntimeException("parsing failure!"); } diff --git a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java index adff502..9ed3578 100644 --- a/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java +++ b/src/org/jetbrains/java/decompiler/modules/renamer/IdentifierConverter.java @@ -387,8 +387,8 @@ public class IdentifierConverter { private void buildInheritanceTree() { - HashMap<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>(); - HashMap<String, StructClass> classes = context.getClasses(); + Map<String, ClassWrapperNode> nodes = new HashMap<String, ClassWrapperNode>(); + Map<String, StructClass> classes = context.getClasses(); List<ClassWrapperNode> rootClasses = new ArrayList<ClassWrapperNode>(); List<ClassWrapperNode> rootInterfaces = new ArrayList<ClassWrapperNode>(); diff --git a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java index 44ec746..819ae64 100644 --- a/src/org/jetbrains/java/decompiler/struct/ContextUnit.java +++ b/src/org/jetbrains/java/decompiler/struct/ContextUnit.java @@ -15,7 +15,7 @@ */ package org.jetbrains.java.decompiler.struct; -import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; +import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader.Link; import org.jetbrains.java.decompiler.util.DataInputFullStream; @@ -23,6 +23,7 @@ import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.jar.JarFile; import java.util.jar.Manifest; public class ContextUnit { @@ -31,168 +32,125 @@ public class ContextUnit { public static final int TYPE_JAR = 1; public static final int TYPE_ZIP = 2; - private static final String MANIFEST_ENTRY = "META-INF/MANIFEST.MF"; + private final int type; + private final boolean own; - // ***************************************************************************** - // private fields - // ***************************************************************************** + private final String archivePath; // relative path to jar/zip + private final String filename; // folder: relative path, archive: file name + private final IResultSaver resultSaver; + private final IDecompiledData decompiledData; - private int type; - - // relative path to jar/zip - private String archivepath; - - // folder: relative path - // archive: file name - private String filename; + private final List<String> classEntries = new ArrayList<String>(); // class file or jar/zip entry + private final List<String> dirEntries = new ArrayList<String>(); + private final List<String[]> otherEntries = new ArrayList<String[]>(); private List<StructClass> classes = new ArrayList<StructClass>(); - - // class file or jar/zip entry. Should, but doesn't have to be the same as qualifiedName of the class - private List<String> classentries = new ArrayList<String>(); - - private List<String> direntries = new ArrayList<String>(); - - private List<String[]> otherentries = new ArrayList<String[]>(); - private Manifest manifest; - private IDecompilatSaver decompilatSaver; - - private IDecompiledData decompiledData; - - private boolean own = true; - - // ***************************************************************************** - // constructors - // ***************************************************************************** - - public ContextUnit(int type, String archivepath, String filename, boolean own, - IDecompilatSaver decompilatSaver, IDecompiledData decompiledData) { + public ContextUnit(int type, String archivePath, String filename, boolean own, IResultSaver resultSaver, IDecompiledData decompiledData) { this.type = type; this.own = own; - this.archivepath = archivepath; + this.archivePath = archivePath; this.filename = filename; - this.decompilatSaver = decompilatSaver; + this.resultSaver = resultSaver; this.decompiledData = decompiledData; } - // ***************************************************************************** - // public methods - // ***************************************************************************** - - public void addClass(StructClass cl, String entryname) { + public void addClass(StructClass cl, String entryName) { classes.add(cl); - classentries.add(entryname); + classEntries.add(entryName); } public void addDirEntry(String entry) { - direntries.add(entry); + dirEntries.add(entry); } - public void addOtherEntry(String fullpath, String entry) { - otherentries.add(new String[]{fullpath, entry}); + public void addOtherEntry(String fullPath, String entry) { + otherEntries.add(new String[]{fullPath, entry}); } public void reload(LazyLoader loader) throws IOException { - List<StructClass> lstClasses = new ArrayList<StructClass>(); + for (StructClass cl : classes) { - String oldname = cl.qualifiedName; + String oldName = cl.qualifiedName; - StructClass newcl; - DataInputFullStream in = loader.getClassStream(oldname); + StructClass newCl; + DataInputFullStream in = loader.getClassStream(oldName); try { - newcl = new StructClass(in, cl.isOwn(), loader); + newCl = new StructClass(in, cl.isOwn(), loader); } finally { in.close(); } - lstClasses.add(newcl); + lstClasses.add(newCl); - Link lnk = loader.getClassLink(oldname); - loader.removeClassLink(oldname); - loader.addClassLink(newcl.qualifiedName, lnk); + Link lnk = loader.getClassLink(oldName); + loader.removeClassLink(oldName); + loader.addClassLink(newCl.qualifiedName, lnk); } classes = lstClasses; } public void save() { - switch (type) { case TYPE_FOLDER: - // create folder - decompilatSaver.saveFolder(filename); + resultSaver.saveFolder(filename); // non-class files - for (String[] arr : otherentries) { - decompilatSaver.copyFile(arr[0], filename, arr[0]); + for (String[] pair : otherEntries) { + resultSaver.copyFile(pair[0], filename, pair[1]); } // classes for (int i = 0; i < classes.size(); i++) { - StructClass cl = classes.get(i); - String entryname = classentries.get(i); - - entryname = decompiledData.getClassEntryName(cl, entryname); - if (entryname != null) { + String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i)); + if (entryName != null) { String content = decompiledData.getClassContent(cl); if (content != null) { - decompilatSaver.saveClassFile(filename, cl.qualifiedName, entryname, content); + resultSaver.saveClassFile(filename, cl.qualifiedName, entryName, content); } } } break; + case TYPE_JAR: case TYPE_ZIP: - // create archive file - decompilatSaver.saveFolder(archivepath); - decompilatSaver.createArchive(archivepath, filename, manifest); + resultSaver.saveFolder(archivePath); + resultSaver.createArchive(archivePath, filename, manifest); // directory entries - for (String direntry : direntries) { - decompilatSaver.saveEntry(archivepath, filename, direntry, null); + for (String dirEntry : dirEntries) { + resultSaver.saveDirEntry(archivePath, filename, dirEntry); } // non-class entries - for (String[] arr : otherentries) { - // manifest was defined by constructor invocation - if (type != TYPE_JAR || !MANIFEST_ENTRY.equalsIgnoreCase(arr[1])) { - decompilatSaver.copyEntry(arr[0], archivepath, filename, arr[1]); + for (String[] pair : otherEntries) { + if (type != TYPE_JAR || !JarFile.MANIFEST_NAME.equalsIgnoreCase(pair[1])) { + resultSaver.copyEntry(pair[0], archivePath, filename, pair[1]); } } // classes for (int i = 0; i < classes.size(); i++) { - StructClass cl = classes.get(i); - String entryname = classentries.get(i); - - entryname = decompiledData.getClassEntryName(cl, entryname); - if (entryname != null) { + String entryName = decompiledData.getClassEntryName(cl, classEntries.get(i)); + if (entryName != null) { String content = decompiledData.getClassContent(cl); - decompilatSaver.saveClassEntry(archivepath, filename, cl.qualifiedName, entryname, content); + resultSaver.saveClassEntry(archivePath, filename, cl.qualifiedName, entryName, content); } } - decompilatSaver.closeArchive(archivepath, filename); + resultSaver.closeArchive(archivePath, filename); } } - // ***************************************************************************** - // private methods - // ***************************************************************************** - - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - public void setManifest(Manifest manifest) { this.manifest = manifest; } @@ -204,16 +162,4 @@ public class ContextUnit { public List<StructClass> getClasses() { return classes; } - - public int getType() { - return type; - } - - public void setDecompilatSaver(IDecompilatSaver decompilatSaver) { - this.decompilatSaver = decompilatSaver; - } - - public void setDecompiledData(IDecompiledData decompiledData) { - this.decompiledData = decompiledData; - } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructClass.java b/src/org/jetbrains/java/decompiler/struct/StructClass.java index 9afe081..cb24ff6 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructClass.java +++ b/src/org/jetbrains/java/decompiler/struct/StructClass.java @@ -24,7 +24,6 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.jetbrains.java.decompiler.util.VBStyleCollection; import java.io.IOException; -import java.io.InputStream; /* class_file { @@ -62,8 +61,8 @@ public class StructClass extends StructMember { private ConstantPool pool; - public StructClass(InputStream inStream, boolean own, LazyLoader loader) throws IOException { - this(new DataInputFullStream(inStream), own, loader); + public StructClass(byte[] bytes, boolean own, LazyLoader loader) throws IOException { + this(new DataInputFullStream(bytes), own, loader); } public StructClass(DataInputFullStream in, boolean own, LazyLoader loader) throws IOException { diff --git a/src/org/jetbrains/java/decompiler/struct/StructContext.java b/src/org/jetbrains/java/decompiler/struct/StructContext.java index da562e9..7bfb139 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructContext.java +++ b/src/org/jetbrains/java/decompiler/struct/StructContext.java @@ -16,65 +16,50 @@ package org.jetbrains.java.decompiler.struct; import org.jetbrains.java.decompiler.main.DecompilerContext; -import org.jetbrains.java.decompiler.main.extern.IDecompilatSaver; -import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; +import org.jetbrains.java.decompiler.main.extern.IResultSaver; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; import org.jetbrains.java.decompiler.util.DataInputFullStream; +import org.jetbrains.java.decompiler.util.InterpreterUtil; import java.io.File; import java.io.IOException; import java.util.Enumeration; import java.util.HashMap; +import java.util.Map; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; - public class StructContext { - // ***************************************************************************** - // private fields - // ***************************************************************************** - - private LazyLoader loader; - - private HashMap<String, StructClass> classes = new HashMap<String, StructClass>(); - - private HashMap<String, ContextUnit> units = new HashMap<String, ContextUnit>(); - - private IDecompilatSaver saver; - - private IDecompiledData decdata; - - public StructContext(IDecompilatSaver saver, IDecompiledData decdata, LazyLoader loader) { + private final IResultSaver saver; + private final IDecompiledData decompiledData; + private final LazyLoader loader; + private final Map<String, ContextUnit> units = new HashMap<String, ContextUnit>(); + private final Map<String, StructClass> classes = new HashMap<String, StructClass>(); + public StructContext(IResultSaver saver, IDecompiledData decompiledData, LazyLoader loader) { this.saver = saver; - this.decdata = decdata; + this.decompiledData = decompiledData; this.loader = loader; - ContextUnit defaultUnit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, "", true, saver, decdata); + ContextUnit defaultUnit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, "", true, saver, decompiledData); units.put("", defaultUnit); } - // ***************************************************************************** - // public methods - // ***************************************************************************** - public StructClass getClass(String name) { return classes.get(name); } public void reloadContext() throws IOException { - for (ContextUnit unit : units.values()) { - for (StructClass cl : unit.getClasses()) { classes.remove(cl.qualifiedName); } unit.reload(loader); - // adjust lobal class collection + // adjust global class collection for (StructClass cl : unit.getClasses()) { classes.put(cl.qualifiedName, cl); } @@ -82,7 +67,6 @@ public class StructContext { } public void saveContext() { - for (ContextUnit unit : units.values()) { if (unit.isOwn()) { unit.save(); @@ -90,134 +74,116 @@ public class StructContext { } } - public void addSpace(File file, boolean isOwn) throws IOException { - addSpace("", file, isOwn); + public void addSpace(File file, boolean isOwn) { + addSpace("", file, isOwn, 0); } - private void addSpace(String path, File file, boolean isOwn) throws IOException { - + private void addSpace(String path, File file, boolean isOwn, int level) { if (file.isDirectory()) { + if (level == 1) path += file.getName(); + else if (level > 1) path += "/" + file.getName(); File[] files = file.listFiles(); - path += "/" + (path.length() == 0 ? "" : file.getName()); - - for (int i = files.length - 1; i >= 0; i--) { - addSpace(path, files[i], isOwn); + if (files != null) { + for (int i = files.length - 1; i >= 0; i--) { + addSpace(path, files[i], isOwn, level + 1); + } } } else { - String filename = file.getName(); boolean isArchive = false; - try { if (filename.endsWith(".jar")) { - addArchive(path, file, ContextUnit.TYPE_JAR, isOwn); isArchive = true; + addArchive(path, file, ContextUnit.TYPE_JAR, isOwn); } else if (filename.endsWith(".zip")) { - addArchive(path, file, ContextUnit.TYPE_ZIP, isOwn); isArchive = true; + addArchive(path, file, ContextUnit.TYPE_ZIP, isOwn); } } catch (IOException ex) { - DecompilerContext.getLogger() - .writeMessage("Invalid archive file: " + (path.length() > 0 ? path + "/" : "") + filename, IFernflowerLogger.ERROR); + String message = "Corrupted archive file: " + file; + DecompilerContext.getLogger().writeMessage(message, ex); + } + if (isArchive) { + return; } - if (!isArchive) { - ContextUnit unit = units.get(path); - if (unit == null) { - unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, path, isOwn, saver, decdata); - units.put(path, unit); - } - - boolean isClass = false; + ContextUnit unit = units.get(path); + if (unit == null) { + unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, path, isOwn, saver, decompiledData); + units.put(path, unit); + } - if (filename.endsWith(".class")) { + if (filename.endsWith(".class")) { + try { + DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null); try { - StructClass cl; - - DataInputFullStream in = loader.getClassStream(file.getAbsolutePath(), null); - try { - cl = new StructClass(in, isOwn, loader); - } - finally { - in.close(); - } - + StructClass cl = new StructClass(in, isOwn, loader); classes.put(cl.qualifiedName, cl); unit.addClass(cl, filename); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.CLASS, file.getAbsolutePath(), null)); - - isClass = true; } - catch (IOException ex) { - DecompilerContext.getLogger().writeMessage("Invalid class file: " + (path.length() > 0 ? path + "/" : "") + filename, - IFernflowerLogger.ERROR); + finally { + in.close(); } } - - if (!isClass) { - unit.addOtherEntry(file.getAbsolutePath(), filename); + catch (IOException ex) { + String message = "Corrupted class file: " + file; + DecompilerContext.getLogger().writeMessage(message, ex); } } + else { + unit.addOtherEntry(file.getAbsolutePath(), filename); + } } } - private void addArchive(String path, File file, int type, boolean isOwn) throws IOException { + @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") + ZipFile archive = type == ContextUnit.TYPE_JAR ? new JarFile(file) : new ZipFile(file); - ZipFile archive; - - if (type == ContextUnit.TYPE_JAR) { // jar - archive = new JarFile(file); - } - else { // zip - archive = new ZipFile(file); - } - - Enumeration<? extends ZipEntry> en = archive.entries(); - while (en.hasMoreElements()) { - ZipEntry entr = en.nextElement(); + try { + Enumeration<? extends ZipEntry> entries = archive.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); - ContextUnit unit = units.get(path + "/" + file.getName()); - if (unit == null) { - unit = new ContextUnit(type, path, file.getName(), isOwn, saver, decdata); - if (type == ContextUnit.TYPE_JAR) { - unit.setManifest(((JarFile)archive).getManifest()); + ContextUnit unit = units.get(path + "/" + file.getName()); + if (unit == null) { + unit = new ContextUnit(type, path, file.getName(), isOwn, saver, decompiledData); + if (type == ContextUnit.TYPE_JAR) { + unit.setManifest(((JarFile)archive).getManifest()); + } + units.put(path + "/" + file.getName(), unit); } - units.put(path + "/" + file.getName(), unit); - } - - String name = entr.getName(); - if (!entr.isDirectory()) { - if (name.endsWith(".class")) { - StructClass cl = new StructClass(archive.getInputStream(entr), isOwn, loader); - classes.put(cl.qualifiedName, cl); - - unit.addClass(cl, name); - if (loader != null) { + String name = entry.getName(); + if (!entry.isDirectory()) { + if (name.endsWith(".class")) { + byte[] bytes = InterpreterUtil.getBytes(archive, entry); + StructClass cl = new StructClass(bytes, isOwn, loader); + classes.put(cl.qualifiedName, cl); + unit.addClass(cl, name); loader.addClassLink(cl.qualifiedName, new LazyLoader.Link(LazyLoader.Link.ENTRY, file.getAbsolutePath(), name)); } + else { + unit.addOtherEntry(file.getAbsolutePath(), name); + } } else { - unit.addOtherEntry(file.getAbsolutePath(), name); + unit.addDirEntry(name); } } - else if (entr.isDirectory()) { - unit.addDirEntry(name); - } + } + finally { + archive.close(); } } - // ***************************************************************************** - // getter and setter methods - // ***************************************************************************** - - public HashMap<String, StructClass> getClasses() { + public Map<String, StructClass> getClasses() { return classes; } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 3ca4a32..2b5249f 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -21,7 +21,6 @@ import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; import org.jetbrains.java.decompiler.util.VBStyleCollection; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -106,7 +105,7 @@ public class StructMethod extends StructMember { public void expandData() throws IOException { if (containsCode && !expanded) { byte[] code = classStruct.getLoader().loadBytecode(this, codeFullLength); - seq = parseBytecode(new DataInputFullStream(new ByteArrayInputStream(code)), codeLength, classStruct.getPool()); + seq = parseBytecode(new DataInputFullStream(code), codeLength, classStruct.getPool()); expanded = true; } } diff --git a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java index 5d43100..2cdc524 100644 --- a/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java +++ b/src/org/jetbrains/java/decompiler/struct/attr/StructGeneralAttribute.java @@ -18,7 +18,6 @@ package org.jetbrains.java.decompiler.struct.attr; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; -import java.io.ByteArrayInputStream; import java.io.IOException; /* @@ -104,7 +103,7 @@ public class StructGeneralAttribute { } protected DataInputFullStream stream() { - return new DataInputFullStream(new ByteArrayInputStream(info)); + return new DataInputFullStream(info); } public void initContent(ConstantPool pool) throws IOException { } diff --git a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java index f610900..70254d8 100644 --- a/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java +++ b/src/org/jetbrains/java/decompiler/struct/lazy/LazyLoader.java @@ -22,14 +22,13 @@ import org.jetbrains.java.decompiler.struct.consts.ConstantPool; import org.jetbrains.java.decompiler.util.DataInputFullStream; import java.io.IOException; -import java.io.InputStream; import java.util.HashMap; import java.util.Map; public class LazyLoader { - private Map<String, Link> mapClassLinks = new HashMap<String, Link>(); - private IBytecodeProvider provider; + private final Map<String, Link> mapClassLinks = new HashMap<String, Link>(); + private final IBytecodeProvider provider; public LazyLoader(IBytecodeProvider provider) { this.provider = provider; @@ -138,10 +137,9 @@ public class LazyLoader { } } - @SuppressWarnings("IOResourceOpenedButNotSafelyClosed") public DataInputFullStream getClassStream(String externalPath, String internalPath) throws IOException { - InputStream stream = provider.getBytecodeStream(externalPath, internalPath); - return stream == null ? null : new DataInputFullStream(stream); + byte[] bytes = provider.getBytecode(externalPath, internalPath); + return new DataInputFullStream(bytes); } public DataInputFullStream getClassStream(String qualifiedClassName) throws IOException { diff --git a/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java b/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java index 79e8c1d..011edde 100644 --- a/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java +++ b/src/org/jetbrains/java/decompiler/util/DataInputFullStream.java @@ -15,14 +15,14 @@ */ package org.jetbrains.java.decompiler.util; +import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; -import java.io.InputStream; public class DataInputFullStream extends DataInputStream { - public DataInputFullStream(InputStream in) { - super(in); + public DataInputFullStream(byte[] bytes) { + super(new ByteArrayInputStream(bytes)); } public int readFull(byte[] b) throws IOException { diff --git a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java index 5f48e32..0047bb7 100644 --- a/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java +++ b/src/org/jetbrains/java/decompiler/util/InterpreterUtil.java @@ -23,6 +23,8 @@ import java.nio.channels.FileChannel; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; public class InterpreterUtil { public static final boolean IS_WINDOWS = System.getProperty("os.name", "").startsWith("Windows"); @@ -59,13 +61,36 @@ public class InterpreterUtil { } } + public static byte[] getBytes(ZipFile archive, ZipEntry entry) throws IOException { + return readAndClose(archive.getInputStream(entry), entry.getSize()); + } + + public static byte[] getBytes(File file) throws IOException { + return readAndClose(new FileInputStream(file), file.length()); + } + + private static byte[] readAndClose(InputStream stream, long length) throws IOException { + try { + byte[] bytes = new byte[(int)length]; + if (stream.read(bytes) != length) { + throw new IOException("premature end of stream"); + } + return bytes; + } + finally { + stream.close(); + } + } + public static String getIndentString(int length) { + if (length == 0) return ""; StringBuilder buf = new StringBuilder(); appendIndent(buf, length); return buf.toString(); } public static void appendIndent(StringBuilder buffer, int length) { + if (length == 0) return; String indent = (String)DecompilerContext.getProperty(IFernflowerPreferences.INDENT_STRING); while (length-- > 0) { buffer.append(indent); diff --git a/test/org/jetbrains/java/decompiler/SingleClassesTest.java b/test/org/jetbrains/java/decompiler/SingleClassesTest.java index 97ad079..e251e91 100644 --- a/test/org/jetbrains/java/decompiler/SingleClassesTest.java +++ b/test/org/jetbrains/java/decompiler/SingleClassesTest.java @@ -44,7 +44,7 @@ public class SingleClassesTest { assertTrue(tempDir.delete()); assertTrue(tempDir.mkdirs()); - decompiler = new ConsoleDecompiler(new HashMap<String, Object>() {{ + decompiler = new ConsoleDecompiler(tempDir, new HashMap<String, Object>() {{ put(IFernflowerPreferences.LOG_LEVEL, "warn"); put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1"); put(IFernflowerPreferences.REMOVE_SYNTHETIC, "1"); @@ -91,7 +91,7 @@ public class SingleClassesTest { decompiler.addSpace(inner, true); } - decompiler.decompileContext(tempDir); + decompiler.decompileContext(); File decompiledFile = new File(tempDir, testName + ".java"); assertTrue(decompiledFile.isFile()); |