From f02eb602c3f8d68e5842cc9dd65718aa9ab425cf Mon Sep 17 00:00:00 2001 From: cpw Date: Fri, 6 Nov 2015 01:36:47 -0500 Subject: [PATCH] Renaming thinger --- .../java/decompiler/main/ClassWriter.java | 10 +- .../decompiler/main/DecompilerContext.java | 19 ++ .../java/decompiler/main/Fernflower.java | 1 + .../main/IdentityRenamerFactory.java | 28 +++ .../main/extern/IVariableNameProvider.java | 11 + .../main/extern/IVariableNamingFactory.java | 7 + .../decompiler/main/rels/MethodWrapper.java | 2 + .../main/rels/NestedClassProcessor.java | 2 +- .../decompiler/vars/VarDefinitionHelper.java | 48 +++-- .../java/decompiler/struct/StructMethod.java | 3 + .../java/decompiler/util/JADNameProvider.java | 193 ++++++++++++++++++ .../decompiler/DecompilerTestFixture.java | 16 +- .../jetbrains/java/decompiler/LVTTest.java | 1 + .../MinecraftDecompilationTest.java | 173 +++------------- 14 files changed, 337 insertions(+), 177 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java create mode 100644 src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java create mode 100644 src/org/jetbrains/java/decompiler/main/extern/IVariableNamingFactory.java create mode 100644 src/org/jetbrains/java/decompiler/util/JADNameProvider.java diff --git a/src/org/jetbrains/java/decompiler/main/ClassWriter.java b/src/org/jetbrains/java/decompiler/main/ClassWriter.java index 808c5e1..c515d2e 100644 --- a/src/org/jetbrains/java/decompiler/main/ClassWriter.java +++ b/src/org/jetbrains/java/decompiler/main/ClassWriter.java @@ -732,7 +732,7 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { buffer.append(' '); String parameterName = methodWrapper.varproc.getVarName(new VarVersionPair(index, 0)); if ((flags & (CodeConstants.ACC_ABSTRACT | CodeConstants.ACC_NATIVE)) != 0) { - parameterName = renameParameterIfPossible(parameterName, index, methodWrapper, flags); + parameterName = methodWrapper.methodStruct.renamer.renameAbstractParameter(parameterName); } buffer.append(parameterName == null ? "param" + index : parameterName); // null iff decompiled with errors @@ -839,13 +839,7 @@ else if (CodeConstants.CLINIT_NAME.equals(name)) { return !hideMethod; } - private String renameParameterIfPossible(String parameterName, int index, MethodWrapper methodWrapper, int flags) { - if (DecompilerContext.getProperty("abstractparamrenamer")==null) return parameterName; - IAbstractParameterRenamer property = (IAbstractParameterRenamer) DecompilerContext.getProperty("abstractparamrenamer"); - return property.renameParameter(parameterName, index, methodWrapper, flags); -} - -private static void mapLines(TextBuffer code, StructLineNumberTableAttribute table, BytecodeMappingTracer tracer, int startLine) { + private static void mapLines(TextBuffer code, StructLineNumberTableAttribute table, BytecodeMappingTracer tracer, int startLine) { // build line start offsets map HashMap> lineStartOffsets = new HashMap>(); for (Map.Entry entry : tracer.getMapping().entrySet()) { diff --git a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java index 15c6e40..8d4d403 100644 --- a/src/org/jetbrains/java/decompiler/main/DecompilerContext.java +++ b/src/org/jetbrains/java/decompiler/main/DecompilerContext.java @@ -21,8 +21,10 @@ import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.extern.IVariableNamingFactory; import org.jetbrains.java.decompiler.modules.renamer.PoolInterceptor; import org.jetbrains.java.decompiler.struct.StructContext; +import org.jetbrains.java.decompiler.util.JADNameProvider; import java.util.HashMap; import java.util.Locale; @@ -36,6 +38,7 @@ public class DecompilerContext { public static final String CURRENT_METHOD_DESCRIPTOR = "CURRENT_METHOD_DESCRIPTOR"; public static final String CURRENT_METHOD_WRAPPER = "CURRENT_METHOD_WRAPPER"; public static final String CURRENT_VAR_PROCESSOR = "CURRENT_VAR_PROCESSOR"; + public static final String RENAMER_FACTORY = "RENAMER_FACTORY"; private static final ThreadLocal currentContext = new ThreadLocal(); @@ -48,6 +51,7 @@ public class DecompilerContext { private PoolInterceptor poolInterceptor; private IFernflowerLogger logger; private BytecodeSourceMapper bytecodeSourceMapper; + private IVariableNamingFactory renamerFactory; private DecompilerContext(Map properties) { this.properties = properties; @@ -59,6 +63,17 @@ public static void initContext(Map propertiesCustom) { properties.putAll(propertiesCustom); } currentContext.set(new DecompilerContext(properties)); + // Default a no-op renamer factory if none is provided + if (DecompilerContext.getProperty(RENAMER_FACTORY) != null) { + try { + currentContext.get().renamerFactory = Class.forName((String) DecompilerContext.getProperty(RENAMER_FACTORY)).asSubclass(IVariableNamingFactory.class).newInstance(); + } catch (Exception e) { + + } + } + if (DecompilerContext.getNamingFactory() == null) { + currentContext.get().renamerFactory = new JADNameProvider.JADNameProviderFactory(); + } } public static DecompilerContext getCurrentContext() { @@ -141,6 +156,10 @@ public static IFernflowerLogger getLogger() { return getCurrentContext().logger; } + public static IVariableNamingFactory getNamingFactory() { + return getCurrentContext().renamerFactory; + } + public static void setLogger(IFernflowerLogger logger) { if (logger != null) { String level = (String)getProperty(IFernflowerPreferences.LOG_LEVEL); diff --git a/src/org/jetbrains/java/decompiler/main/Fernflower.java b/src/org/jetbrains/java/decompiler/main/Fernflower.java index 6afe975..91e70d3 100644 --- a/src/org/jetbrains/java/decompiler/main/Fernflower.java +++ b/src/org/jetbrains/java/decompiler/main/Fernflower.java @@ -27,6 +27,7 @@ import org.jetbrains.java.decompiler.struct.StructClass; import org.jetbrains.java.decompiler.struct.StructContext; import org.jetbrains.java.decompiler.struct.lazy.LazyLoader; +import org.jetbrains.java.decompiler.util.JADNameProvider; import java.io.File; import java.util.HashSet; diff --git a/src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java b/src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java new file mode 100644 index 0000000..bc29407 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/main/IdentityRenamerFactory.java @@ -0,0 +1,28 @@ +package org.jetbrains.java.decompiler.main; + +import java.util.Map; + +import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; +import org.jetbrains.java.decompiler.main.extern.IVariableNamingFactory; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.StructMethod; + +public class IdentityRenamerFactory implements IVariableNamingFactory, IVariableNameProvider { + @Override + public IVariableNameProvider createFactory(StructMethod method) { + return this; + } + + @Override + public String renameAbstractParameter(String abstractParam) { + return abstractParam; + } + + @Override + public Map rename(Map variables) { + return null; + } + @Override + public void addParentContext(IVariableNameProvider renamer) { + } +} diff --git a/src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java b/src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java new file mode 100644 index 0000000..9d6b1a5 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/main/extern/IVariableNameProvider.java @@ -0,0 +1,11 @@ +package org.jetbrains.java.decompiler.main.extern; + +import java.util.Map; + +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; + +public interface IVariableNameProvider { + public Map rename(Map variables); + public String renameAbstractParameter(String abstractParam); + public void addParentContext(IVariableNameProvider renamer); +} diff --git a/src/org/jetbrains/java/decompiler/main/extern/IVariableNamingFactory.java b/src/org/jetbrains/java/decompiler/main/extern/IVariableNamingFactory.java new file mode 100644 index 0000000..2f6c3ec --- /dev/null +++ b/src/org/jetbrains/java/decompiler/main/extern/IVariableNamingFactory.java @@ -0,0 +1,7 @@ +package org.jetbrains.java.decompiler.main.extern; + +import org.jetbrains.java.decompiler.struct.StructMethod; + +public interface IVariableNamingFactory { + public IVariableNameProvider createFactory(StructMethod structMethod); +} diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java index 0f44e42..c277392 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodWrapper.java @@ -15,7 +15,9 @@ */ package org.jetbrains.java.decompiler.main.rels; +import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.CounterContainer; +import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph; import org.jetbrains.java.decompiler.modules.decompiler.sforms.FlattenStatementsHelper; import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java index 3975d8c..07ac606 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -577,7 +577,7 @@ private static void insertLocalVars(final ClassNode parent, final ClassNode chil for (final MethodWrapper meth : child.getWrapper().getMethods()) { if (meth.root != null) { // neither abstract nor native - + if (encmeth != null) { meth.methodStruct.renamer.addParentContext(encmeth.methodStruct.renamer); } // local var names HashMap mapNewNames = new HashMap(); // local var types diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java index 164ddb5..59b1c42 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/vars/VarDefinitionHelper.java @@ -18,6 +18,7 @@ import org.jetbrains.java.decompiler.code.CodeConstants; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor; import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent; import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent; @@ -424,35 +425,47 @@ private static boolean setDefinition(Exprent expr, Integer index) { private void propogateLVTs(Statement stat) { MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); - Map types = new HashMap(); + Map types = new LinkedHashMap(); int index = 0; if (!mt.hasModifier(CodeConstants.ACC_STATIC)) { - types.put(new VarVersionPair(index, 0), new VarInfo(varproc.getLVT().getCandidates(index++).get(0), new VarType(mt.getClassStruct().qualifiedName))); + types.put(new VarVersionPair(index++, 0), new VarInfo(null,null)); } for (VarType var : md.params) { List vars = varproc.getLVT().getCandidates(index); if (vars != null) { - types.put(new VarVersionPair(index, 0), new VarInfo(vars.get(0), var)); + types.put(new VarVersionPair(index, 0), new VarInfo(null,null)); } index += var.stackSize; } findTypes(stat, types); - //renameTypes(types); - + Map typeNames = new LinkedHashMap(); for (Entry e : types.entrySet()) { - if (e.getValue().lvt != null) { - varproc.setVarLVT(e.getKey(), e.getValue().lvt); - } + typeNames.put(e.getKey(), e.getValue().typeName()); } - + StructMethod current_meth = (StructMethod)DecompilerContext.getProperty(DecompilerContext.CURRENT_METHOD); + Map renames = current_meth.renamer.rename(typeNames); Map lvts = new HashMap(); + for (Entry e : types.entrySet()) { - if (e.getValue().lvt != null) { - lvts.put(e.getKey(), e.getValue().lvt); + VarVersionPair idx = e.getKey(); + // skip this. we can't rename it + if (idx.var == 0 && !mt.hasModifier(CodeConstants.ACC_STATIC)) { + continue; + } + LVTVariable lvt = e.getValue().lvt; + if (renames!=null) { + varproc.setVarName(idx, renames.get(idx)); + } + if (lvt != null) { + if (renames!=null) { + lvt = lvt.rename(renames.get(idx)); + } + varproc.setVarLVT(idx, lvt); + lvts.put(idx, lvt); } } @@ -508,14 +521,21 @@ private static class VarInfo { String cast; private VarInfo(LVTVariable lvt, VarType type) { if (lvt != null && lvt.getSig() != null) { - cast = ExprProcessor.getCastTypeName(GenericType.parse(lvt.getSig())); + cast = ExprProcessor.getCastTypeName(GenericType.parse(lvt.getSig()),false); } else if (lvt != null) { - cast = ExprProcessor.getCastTypeName(lvt.getVarType()); + cast = ExprProcessor.getCastTypeName(lvt.getVarType(),false); + } + else if (type != null) { + cast = ExprProcessor.getCastTypeName(type,false); } else { - cast = ExprProcessor.getCastTypeName(type); + cast = "this"; } + this.lvt = lvt; + } + public String typeName() { + return cast; } } diff --git a/src/org/jetbrains/java/decompiler/struct/StructMethod.java b/src/org/jetbrains/java/decompiler/struct/StructMethod.java index 8f0da2b..7e7f758 100644 --- a/src/org/jetbrains/java/decompiler/struct/StructMethod.java +++ b/src/org/jetbrains/java/decompiler/struct/StructMethod.java @@ -18,6 +18,7 @@ import org.jetbrains.java.decompiler.code.*; import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; +import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; import org.jetbrains.java.decompiler.struct.attr.StructGeneralAttribute; import org.jetbrains.java.decompiler.struct.attr.StructGenericSignatureAttribute; import org.jetbrains.java.decompiler.struct.consts.ConstantPool; @@ -60,6 +61,7 @@ public class StructMethod extends StructMember { private InstructionSequence seq; private boolean expanded = false; private VBStyleCollection codeAttributes; + public final IVariableNameProvider renamer; public StructMethod(DataInputFullStream in, StructClass clStruct) throws IOException { classStruct = clStruct; @@ -78,6 +80,7 @@ public StructMethod(DataInputFullStream in, StructClass clStruct) throws IOExcep attributes.addAllWithKey(codeAttributes); codeAttributes = null; } + this.renamer = DecompilerContext.getNamingFactory().createFactory(this); } @Override diff --git a/src/org/jetbrains/java/decompiler/util/JADNameProvider.java b/src/org/jetbrains/java/decompiler/util/JADNameProvider.java new file mode 100644 index 0000000..010ef5f --- /dev/null +++ b/src/org/jetbrains/java/decompiler/util/JADNameProvider.java @@ -0,0 +1,193 @@ +package org.jetbrains.java.decompiler.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +import org.jetbrains.java.decompiler.main.extern.IVariableNameProvider; +import org.jetbrains.java.decompiler.main.extern.IVariableNamingFactory; +import org.jetbrains.java.decompiler.main.rels.MethodWrapper; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair; +import org.jetbrains.java.decompiler.struct.StructMethod; + +public class JADNameProvider implements IVariableNameProvider { + private HashMap last = null; + private HashMap remap = null; + private static final Pattern CAPS_START = Pattern.compile("^[A-Z]"); + private static final Pattern ARRAY = Pattern.compile("(\\[|\\.\\.\\.)"); + + public JADNameProvider(StructMethod wrapper) { + last = new HashMap(); + last.put("int", new Holder(0, true, "i", "j", "k", "l")); + last.put("byte", new Holder(0, false, "b" )); + last.put("char", new Holder(0, false, "c" )); + last.put("short", new Holder(1, false, "short" )); + last.put("boolean", new Holder(0, true, "flag" )); + last.put("double", new Holder(0, false, "d" )); + last.put("float", new Holder(0, true, "f" )); + last.put("File", new Holder(1, true, "file" )); + last.put("String", new Holder(0, true, "s" )); + last.put("Class", new Holder(0, true, "oclass" )); + last.put("Long", new Holder(0, true, "olong" )); + last.put("Byte", new Holder(0, true, "obyte" )); + last.put("Short", new Holder(0, true, "oshort" )); + last.put("Boolean", new Holder(0, true, "obool" )); + last.put("Package", new Holder(0, true, "opackage")); + last.put("Enum", new Holder(0, true, "oenum" )); + + remap = new HashMap(); + remap.put("long", "int"); + } + + public void addParentContext(IVariableNameProvider iparent) { + JADNameProvider parent = (JADNameProvider) iparent; + last = new HashMap(); + for (Entry e : parent.last.entrySet()) { + Holder v = e.getValue(); + last.put(e.getKey(), new Holder(v.id, v.skip_zero, v.names)); + } + + remap = new HashMap(); + for (Entry e : parent.remap.entrySet()) { + remap.put(e.getKey(), e.getValue()); + } + } + + private static class Holder { + public int id; + public boolean skip_zero; + public final List names = new ArrayList(); + + public Holder(int t1, boolean skip_zero, String... names) { + this.id = t1; + this.skip_zero = skip_zero; + Collections.addAll(this.names, names); + } + + public Holder(int t1, boolean skip_zero, List names) { + this.id = t1; + this.skip_zero = skip_zero; + this.names.addAll(names); + } + } + + public Map rename(Map entries) { + List keys = new ArrayList(entries.keySet()); + Collections.sort(keys, new Comparator(){ + @Override + public int compare(VarVersionPair o1, VarVersionPair o2) { + if (o1.var != o2.var) return o1.var - o2.var; + return o1.version - o2.version; + } + }); + Map result = new LinkedHashMap(); + for (VarVersionPair ver : keys) { + String type = entries.get(ver); + if ("this".equals(type)) { + continue; + } + if (type.indexOf('<') != -1) { + type = type.substring(0, type.indexOf('<') - 1); + } + if (type.indexOf('.') != -1) { + type = type.substring(type.lastIndexOf('.')+1); + } + result.put(ver, getNewName(type)); + } + return result; + } + + protected String getNewName(String type) { + String index = null; + String findtype = type; + + while (findtype.contains("[][]")) + { + findtype = findtype.replaceAll("\\[\\]\\[\\]", "[]"); + } + if (last.containsKey(findtype)) + { + index = findtype; + } + else if (last.containsKey(findtype.toLowerCase(Locale.ENGLISH))) + { + index = findtype.toLowerCase(Locale.ENGLISH); + } + else if (remap.containsKey(type)) + { + index = remap.get(type); + } + + if ((index == null || index.length() == 0) && (CAPS_START.matcher(type).find() || ARRAY.matcher(type).find())) + { + // replace multi things with arrays. + type = type.replace("...", "[]"); + + while (type.contains("[][]")) + { + type = type.replaceAll("\\[\\]\\[\\]", "[]"); + } + + String name = type.toLowerCase(Locale.ENGLISH); + // Strip single dots that might happen because of inner class references + name = name.replace(".", ""); + boolean skip_zero = true; + + if (Pattern.compile("\\[").matcher(type).find()) + { + skip_zero = true; + name = "a" + name; + name = name.replace("[]", "").replace("...", ""); + } + + last.put(type.toLowerCase(Locale.ENGLISH), new Holder(0, skip_zero, name)); + index = type.toLowerCase(Locale.ENGLISH); + } + + if (index == null || index.length() == 0) + { + return type.toLowerCase(Locale.ENGLISH); + } + + Holder holder = last.get(index); + int id = holder.id; + List names = holder.names; + + int ammount = names.size(); + + String name; + if (ammount == 1) + { + name = names.get(0) + (id == 0 && holder.skip_zero ? "" : id); + } + else + { + int num = id / ammount; + name = names.get(id % ammount) + (id < ammount && holder.skip_zero ? "" : num); + } + + holder.id++; + return name; + } + + @Override + public String renameAbstractParameter(String abstractParam) { + return abstractParam; + } + + + public static class JADNameProviderFactory implements IVariableNamingFactory { + @Override + public IVariableNameProvider createFactory(StructMethod method) { + return new JADNameProvider(method); + } + + } +} \ No newline at end of file diff --git a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java index 77eda87..f2c344a 100644 --- a/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java +++ b/test/org/jetbrains/java/decompiler/DecompilerTestFixture.java @@ -46,26 +46,23 @@ public void setUp(final Map options) throws IOException { assertTrue("current dir: " + new File("").getAbsolutePath(), isTestDataDir(testDataDir)); //noinspection SSBasedInspection - tempDir = getRandomDir(); - if (tempDir.exists()) tempDir.delete(); + tempDir = File.createTempFile("decompiler_test_", "_dir"); + assertTrue(tempDir.delete()); + targetDir = new File(tempDir, "decompiled"); - targetDir.mkdirs(); + assertTrue(targetDir.mkdirs()); decompiler = new ConsoleDecompiler(this.targetDir, new HashMap() {{ put(IFernflowerPreferences.LOG_LEVEL, "warn"); put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES, "1"); put(IFernflowerPreferences.REMOVE_SYNTHETIC, "1"); put(IFernflowerPreferences.REMOVE_BRIDGE, "1"); put(IFernflowerPreferences.LITERALS_AS_IS, "1"); - put(IFernflowerPreferences.MAX_PROCESSING_METHOD, "60000"); + put(IFernflowerPreferences.UNIT_TEST_MODE, "1"); putAll(options); }}); } - protected File getRandomDir() throws IOException { - return File.createTempFile("decompiler_test_", "_dir"); - } - -public void tearDown() { + public void tearDown() { if (tempDir != null && cleanup) { delete(tempDir); } @@ -98,5 +95,6 @@ private static void delete(File file) { for (File f : files) delete(f); } } + assertTrue(file.delete()); } } diff --git a/test/org/jetbrains/java/decompiler/LVTTest.java b/test/org/jetbrains/java/decompiler/LVTTest.java index 368407e..ff2457b 100644 --- a/test/org/jetbrains/java/decompiler/LVTTest.java +++ b/test/org/jetbrains/java/decompiler/LVTTest.java @@ -59,4 +59,5 @@ public void setUp() throws IOException { // @Test public void testMCAbstractResourcePack() { doTest("net/minecraft/client/resources/AbstractResourcePack"); } // @Test public void testMCGuiShareToLan() { doTest("net/minecraft/client/gui/GuiShareToLan"); } // @Test public void testMCContainerPlayer() { doTest("net/minecraft/inventory/ContainerPlayer"); } + @Test public void testMCContainerPlayer() { doTest("net/minecraft/inventory/ContainerPlayer"); } } diff --git a/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java index 2ae6da5..087c810 100644 --- a/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java +++ b/test/org/jetbrains/java/decompiler/MinecraftDecompilationTest.java @@ -16,83 +16,47 @@ package org.jetbrains.java.decompiler; import org.hamcrest.Matchers; -import org.jetbrains.java.decompiler.code.CodeConstants; -import org.jetbrains.java.decompiler.main.DecompilerContext; import org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler; -import org.jetbrains.java.decompiler.main.extern.IAbstractParameterRenamer; import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences; -import org.jetbrains.java.decompiler.main.rels.MethodWrapper; import org.jetbrains.java.decompiler.util.InterpreterUtil; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.io.*; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Map.Entry; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + public class MinecraftDecompilationTest { - public static final Pattern p = Pattern.compile("func_(\\d+)_.*"); private DecompilerTestFixture fixture; - public static final int LOOPS = Integer.parseInt(System.getProperty("fftestloops","50")); - public static final String OUTROOT = System.getProperty("fftestout","C:/TEMP/FFTEST"); - public static final String MD5IN = System.getProperty("ffmd5in",null); private static final String MC_JAR = "minecraft_ff_in.jar"; @Before public void setUp() throws IOException { - fixture = new DecompilerTestFixture() { - @Override - public File getRandomDir() { - return new File(OUTROOT,"fftest"); - } - }; + fixture = new DecompilerTestFixture(); + // -din=1 -rbr=0 -dgs=1 -asc=1 -rsy=0 Map mcFFOptions = new HashMap() {{ put(IFernflowerPreferences.DECOMPILE_INNER,"1"); put(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES,"1"); put(IFernflowerPreferences.ASCII_STRING_CHARACTERS,"1"); put(IFernflowerPreferences.INCLUDE_ENTIRE_CLASSPATH, "1"); - put(IFernflowerPreferences.REMOVE_BRIDGE, "1"); - put(IFernflowerPreferences.REMOVE_SYNTHETIC, "1"); - put(IFernflowerPreferences.NEW_LINE_SEPARATOR, "1"); - put(IFernflowerPreferences.LITERALS_AS_IS, "0"); }}; fixture.setUp(mcFFOptions); - DecompilerContext.setProperty("abstractparamrenamer", new IAbstractParameterRenamer() { - @Override - public String renameParameter(String orig, int index, MethodWrapper wrapper, int flags) { - String result = orig; - if ((flags & CodeConstants.ACC_ABSTRACT) != 0) { - String methName = wrapper.methodStruct.getName(); - Matcher m = p.matcher(methName); - if (m.matches()) { - result = String.format("p_%s_%d_", m.group(1),index); - } - } - return result; - } - }); if (!new File(fixture.getTestDataDir(), MC_JAR).exists()) { - throw new RuntimeException("Missing "+MC_JAR+" in testData dir - aborting"); + throw new RuntimeException("Missing "+MC_JAR+" in testData dir - aborting"); } } @After public void tearDown() { - fixture.tearDown(); - fixture = null; +// fixture.tearDown(); +// fixture = null; } // @Test @@ -108,116 +72,32 @@ public void tearDown() { // } @Test - public void testJar() throws IOException { - Map md5s = new HashMap(); - MessageDigest md5; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException e1) { - md5 = null; - } - System.out.printf("TEST SETUP: MD5: %s, OUTPUT: %s, LOOPS %d\n",MD5IN, OUTROOT, LOOPS); + public void testJar() { ConsoleDecompiler decompiler = fixture.getDecompiler(); - Map valid = new HashMap(); - if (MD5IN != null) { - byte[] bytes = InterpreterUtil.getBytes(new File(MD5IN)); - String md5list = new String(bytes,"UTF-8"); - for (String line : md5list.split("\n")) { - String[] parts = line.split(","); - md5s.put(parts[0],parts[1]); - } - } else { - - System.out.println("Decompiling base"); - decompiler.addSpace(new File(fixture.getTestDataDir(), MC_JAR), true); - decompiler.decompileContext(); - - - readJar(new File(fixture.getTargetDir(), MC_JAR),md5s,md5); - File outmd5 = new File(fixture.getRandomDir(),"md5s.csv"); - FileWriter fos = new FileWriter(outmd5); - for (Entry md5sum : md5s.entrySet()) { - fos.write(String.format("%s,%s\n", md5sum.getKey(),md5sum.getValue())); - } - fos.close(); - } - Map> variants = new HashMap>(); - File outRoot = new File(OUTROOT,"ffbulk"); - outRoot.mkdirs(); - for (int x = 0; x < LOOPS; x++) { - this.tearDown(); - this.setUp(); - System.out.printf("%d/%s Starting Decompile",x,LOOPS); - decompiler = fixture.getDecompiler(); - decompiler.addSpace(new File(fixture.getTestDataDir(), MC_JAR), true); - decompiler.decompileContext(); - System.gc(); + decompiler.addSpace(new File(fixture.getTestDataDir(), MC_JAR), true); + decompiler.decompileContext(); - Map data = readJar(new File(fixture.getTargetDir(), MC_JAR),null,null); + File unpacked = new File(fixture.getTempDir(), "unpacked"); + unpack(new File(fixture.getTargetDir(), "bulk.jar"), unpacked); - for (Entry e : data.entrySet()) { - String fname = e.getKey(); - String found = e.getValue(); - String md5digest = md5digest(found, md5); - String expected = null; - if (md5digest.equals(md5s.get(fname)) && !valid.containsKey(fname)) { - valid.put(fname, found); - expected = found; - } else if (valid.containsKey(fname)) { - expected = valid.get(fname); - } - HashSet set = variants.get(fname); - if (set == null) { - set = new HashSet(); - set.add(md5s.get(fname)); - variants.put(fname, set); - } - if (!set.contains(md5digest)) { - System.out.println("New Variant: " + fname); - set.add(md5digest); - System.out.println("Orig md5:"+md5s.get(fname)); - System.out.println("Variant md5: "+md5digest); - if (expected != null) writeFile(expected,fname,outRoot); - writeFile(found,fname+"."+md5digest,outRoot); - } else if (!md5digest.equals(md5s.get(fname))) { - System.out.println("Existing Variant: " + fname); - System.out.println("Variant md5: "+md5digest); - } - } - } // compareDirectories(new File(fixture.getTestDataDir(), "bulk"), unpacked); } - private static void writeFile(String fl, String path, File outRoot) { - File out = new File(outRoot,path); - out.getParentFile().mkdirs(); - try { - FileWriter fw = new FileWriter(out.getAbsoluteFile()); - fw.write(fl); - fw.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - private static Map readJar(File archive, Map md5s, MessageDigest md5) { - Map ret = new HashMap(); + private static void unpack(File archive, File targetDir) { try { ZipFile zip = new ZipFile(archive); try { Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); - if (!entry.isDirectory() && entry.getName().endsWith(".java")) { + if (!entry.isDirectory()) { + File file = new File(targetDir, entry.getName()); + assertTrue(file.getParentFile().mkdirs() || file.getParentFile().isDirectory()); InputStream in = zip.getInputStream(entry); - ByteArrayOutputStream out = new ByteArrayOutputStream((int)entry.getSize()); + OutputStream out = new FileOutputStream(file); InterpreterUtil.copyStream(in, out); out.close(); in.close(); - String fileContent = new String(out.toByteArray()); - ret.put(entry.getName(), fileContent); - if (md5!=null && md5s != null) { - md5s.put(entry.getName(), md5digest(fileContent, md5)); - } } } } @@ -228,14 +108,17 @@ private static Map readJar(File archive, Map md5 catch (IOException e) { throw new RuntimeException(e); } - return ret; } - private static String md5digest(String input, MessageDigest md5) { - md5.reset(); - try { - return String.format("0%032x",new BigInteger(1,md5.digest(input.getBytes("UTF-8")))); - } catch (UnsupportedEncodingException e) { - return null; + + private static void compareDirectories(File expected, File actual) { + String[] expectedList = expected.list(); + String[] actualList = actual.list(); + assertThat(actualList, Matchers.arrayContainingInAnyOrder(expectedList)); + for (String name : expectedList) { + File child = new File(expected, name); + if (child.isDirectory()) { + compareDirectories(child, new File(actual, name)); + } } } }