summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar jeff2015-01-19 22:22:57 -0500
committerGravatar jeff2015-01-19 22:22:57 -0500
commit2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c (patch)
tree0e9c23d6838d3e0299831dbc24b6d736c268cd8b
parentadded inverse operation for moving classes out of the default package (diff)
downloadenigma-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.tar.gz
enigma-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.tar.xz
enigma-2fbcf8e5c4eec0aa4a4fc59c7cc8abac33b1429c.zip
solved tricky issue with incorrect translation of fields/methods referenced by a subclass instead of the declaring class
-rw-r--r--build.py79
-rw-r--r--libs/proguard.jarbin0 -> 853203 bytes
-rw-r--r--proguard.conf7
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java8
-rw-r--r--src/cuchaz/enigma/Main.java2
-rw-r--r--src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java65
-rw-r--r--src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java4
-rw-r--r--src/cuchaz/enigma/analysis/TranslationIndex.java172
-rw-r--r--src/cuchaz/enigma/bytecode/ClassTranslator.java9
-rw-r--r--src/cuchaz/enigma/mapping/JavassistUtil.java55
-rw-r--r--src/cuchaz/enigma/mapping/Mappings.java11
-rw-r--r--src/cuchaz/enigma/mapping/MappingsRenamer.java6
-rw-r--r--src/cuchaz/enigma/mapping/Translator.java57
-rw-r--r--ssjb.py223
-rw-r--r--test/cuchaz/enigma/TestDeobfuscator.java2
-rw-r--r--test/cuchaz/enigma/TestInnerClasses.java2
-rw-r--r--test/cuchaz/enigma/TestJarIndexConstructorReferences.java2
-rw-r--r--test/cuchaz/enigma/TestJarIndexInheritanceTree.java30
-rw-r--r--test/cuchaz/enigma/TestJarIndexLoneClass.java15
-rw-r--r--test/cuchaz/enigma/TestSourceIndex.java1
-rw-r--r--test/cuchaz/enigma/TestTokensConstructors.java2
22 files changed, 472 insertions, 284 deletions
diff --git a/build.py b/build.py
index 62b96d11..b9d1121f 100644
--- a/build.py
+++ b/build.py
@@ -19,42 +19,65 @@ def getJarFullName(name=None) :
19 return "%s-%s.jar" % (projectName, version) 19 return "%s-%s.jar" % (projectName, version)
20 20
21def buildGuiJar(): 21def buildGuiJar():
22 jarName = "gui" 22 jarName = "gui"
23 os.makedirs(dirTemp) 23 os.makedirs(dirTemp)
24 ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin)) 24 ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin))
25 ssjb.unpackJars(dirTemp, "ivy/bundles", recursive=True) 25 ssjb.unpackJars(dirTemp, "ivy/bundles", recursive=True)
26 ssjb.unpackJars(dirTemp, "ivy/jars", recursive=True) 26 ssjb.unpackJars(dirTemp, "ivy/jars", recursive=True)
27 ssjb.unpackJars(dirTemp, "libs", recursive=True) 27 ssjb.unpackJars(dirTemp, "libs", recursive=True)
28 ssjb.delete(os.path.join(dirTemp, "LICENSE.txt")) 28 ssjb.delete(os.path.join(dirTemp, "LICENSE.txt"))
29 ssjb.copyFile(dirTemp, "license.APL2.txt") 29 ssjb.copyFile(dirTemp, "license.APL2.txt")
30 ssjb.copyFile(dirTemp, "license.GPL3.txt") 30 ssjb.copyFile(dirTemp, "license.GPL3.txt")
31 ssjb.copyFile(dirTemp, "readme.txt") 31 ssjb.copyFile(dirTemp, "readme.txt")
32 manifest = ssjb.buildManifest(projectName, version, author, "cuchaz.enigma.Main") 32 manifest = ssjb.buildManifest(projectName, version, author, "cuchaz.enigma.Main")
33 ssjb.jar(os.path.join(dirBuild, getJarFullName()), dirTemp, manifest=manifest) 33 ssjb.jar(os.path.join(dirBuild, getJarFullName()), dirTemp, manifest=manifest)
34 ssjb.delete(dirTemp) 34 ssjb.delete(dirTemp)
35 35
36def buildTranslateJar(): 36def buildTranslateJar():
37 jarName = "translate" 37 jarName = "translate"
38 os.makedirs(dirTemp) 38 os.makedirs(dirTemp)
39 files = ssjb.findFiles(dirBin, "cuchaz/enigma/mapping/*") 39 files = ssjb.findFiles(dirBin, "cuchaz/enigma/mapping/*")
40 files += ssjb.findFiles(dirBin, "cuchaz/enigma/bytecode/*") 40 files += ssjb.findFiles(dirBin, "cuchaz/enigma/bytecode/*")
41 ssjb.copyFiles(dirTemp, dirBin, files) 41 ssjb.copyFiles(dirTemp, dirBin, files)
42 ssjb.copyFile(dirTemp, "license.GPL3.txt", renameTo="license.txt") 42 ssjb.copyFile(dirTemp, "license.GPL3.txt", renameTo="license.txt")
43 ssjb.copyFile(dirTemp, "readme.translate.txt", renameTo="readme.txt") 43 ssjb.copyFile(dirTemp, "readme.translate.txt", renameTo="readme.txt")
44 manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author) 44 manifest = ssjb.buildManifest("%s-%s" % (projectName, jarName), version, author)
45 ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest) 45 ssjb.jar(os.path.join(dirBuild, getJarFullName(jarName)), dirTemp, manifest=manifest)
46 ssjb.delete(dirTemp) 46 ssjb.delete(dirTemp)
47 47
48def taskMain(): 48def taskMain():
49 ssjb.delete(dirBuild) 49 ssjb.delete(dirBuild)
50 os.makedirs(dirBuild) 50 os.makedirs(dirBuild)
51 buildGuiJar() 51 buildGuiJar()
52 buildTranslateJar() 52 buildTranslateJar()
53 53
54 54
55def makeTestJar(name, glob):
56
57 pathJar = os.path.join(dirBuild, "%s.jar" % name)
58 pathObfJar = os.path.join(dirBuild, "%s.obf.jar" % name)
59
60 # build the deobf jar
61 ssjb.delete(dirTemp)
62 os.makedirs(dirTemp)
63 ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin, "cuchaz/enigma/inputs/Keep.class"))
64 ssjb.copyFiles(dirTemp, dirBin, ssjb.findFiles(dirBin, glob))
65 ssjb.jar(pathJar, dirTemp)
66 ssjb.delete(dirTemp)
67
68 # build the obf jar
69 ssjb.callJavaJar("libs/proguard.jar", ["@proguard.conf", "-injars", pathJar, "-outjars", pathObfJar])
70
71def taskBuildTestJars():
72 makeTestJar("testLoneClass", "cuchaz/enigma/inputs/loneClass/*.class")
73 makeTestJar("testConstructors", "cuchaz/enigma/inputs/constructors/*.class")
74 makeTestJar("testInheritanceTree", "cuchaz/enigma/inputs/inheritanceTree/*.class")
75 makeTestJar("testInnerClasses", "cuchaz/enigma/inputs/innerClasses/*.class")
76
55ssjb.registerTask("main", taskMain) 77ssjb.registerTask("main", taskMain)
78ssjb.registerTask("buildTestJars", taskBuildTestJars)
56 79
57 80
58if __name__ == "__main__": 81if __name__ == "__main__":
59 ssjb.run() 82 ssjb.run()
60 83
diff --git a/libs/proguard.jar b/libs/proguard.jar
new file mode 100644
index 00000000..a948c89b
--- /dev/null
+++ b/libs/proguard.jar
Binary files differ
diff --git a/proguard.conf b/proguard.conf
new file mode 100644
index 00000000..e1f04aee
--- /dev/null
+++ b/proguard.conf
@@ -0,0 +1,7 @@
1-libraryjars <java.home>/lib/rt.jar
2-overloadaggressively
3-repackageclasses
4-allowaccessmodification
5-dontoptimize
6-dontshrink
7-keep class cuchaz.enigma.inputs.Keep \ No newline at end of file
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 679518a4..23057223 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -144,7 +144,7 @@ public class Deobfuscator {
144 // fields 144 // fields
145 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { 145 for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) {
146 FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName()); 146 FieldEntry fieldEntry = new FieldEntry(obfClassEntry, fieldMapping.getObfName());
147 ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(fieldEntry); 147 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(fieldEntry);
148 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) { 148 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(fieldEntry.getClassEntry())) {
149 boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry); 149 boolean wasMoved = renamer.moveFieldToObfClass(classMapping, fieldMapping, resolvedObfClassEntry);
150 if (wasMoved) { 150 if (wasMoved) {
@@ -167,7 +167,7 @@ public class Deobfuscator {
167 methodMapping.getObfName(), 167 methodMapping.getObfName(),
168 methodMapping.getObfSignature() 168 methodMapping.getObfSignature()
169 ); 169 );
170 ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(methodEntry); 170 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(methodEntry);
171 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) { 171 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(methodEntry.getClassEntry())) {
172 boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry); 172 boolean wasMoved = renamer.moveMethodToObfClass(classMapping, methodMapping, resolvedObfClassEntry);
173 if (wasMoved) { 173 if (wasMoved) {
@@ -233,7 +233,7 @@ public class Deobfuscator {
233 public Translator getTranslator(TranslationDirection direction) { 233 public Translator getTranslator(TranslationDirection direction) {
234 Translator translator = m_translatorCache.get(direction); 234 Translator translator = m_translatorCache.get(direction);
235 if (translator == null) { 235 if (translator == null) {
236 translator = m_mappings.getTranslator(direction); 236 translator = m_mappings.getTranslator(direction, m_jarIndex.getTranslationIndex());
237 m_translatorCache.put(direction, translator); 237 m_translatorCache.put(direction, translator);
238 } 238 }
239 return translator; 239 return translator;
@@ -311,7 +311,7 @@ public class Deobfuscator {
311 Entry obfEntry = obfuscateEntry(deobfReference.entry); 311 Entry obfEntry = obfuscateEntry(deobfReference.entry);
312 312
313 // try to resolve the class 313 // try to resolve the class
314 ClassEntry resolvedObfClassEntry = m_jarIndex.resolveEntryClass(obfEntry); 314 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(obfEntry);
315 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { 315 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) {
316 // change the class of the entry 316 // change the class of the entry
317 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); 317 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry);
diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java
index f8d3afe2..1891ded8 100644
--- a/src/cuchaz/enigma/Main.java
+++ b/src/cuchaz/enigma/Main.java
@@ -28,7 +28,7 @@ public class Main {
28 } 28 }
29 29
30 // DEBUG 30 // DEBUG
31 // gui.getController().openDeclaration( new ClassEntry( "none/ces" ) ); 31 //gui.getController().openDeclaration(new ClassEntry("none/bxq"));
32 } 32 }
33 33
34 private static File getFile(String path) { 34 private static File getFile(String path) {
diff --git a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
index b132305c..3eaa3912 100644
--- a/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
+++ b/src/cuchaz/enigma/analysis/ClassInheritanceTreeNode.java
@@ -51,8 +51,8 @@ public class ClassInheritanceTreeNode extends DefaultMutableTreeNode {
51 public void load(TranslationIndex ancestries, boolean recurse) { 51 public void load(TranslationIndex ancestries, boolean recurse) {
52 // get all the child nodes 52 // get all the child nodes
53 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList(); 53 List<ClassInheritanceTreeNode> nodes = Lists.newArrayList();
54 for (String subclassName : ancestries.getSubclassNames(m_obfClassName)) { 54 for (ClassEntry subclassEntry : ancestries.getSubclass(new ClassEntry(m_obfClassName))) {
55 nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassName)); 55 nodes.add(new ClassInheritanceTreeNode(m_deobfuscatingTranslator, subclassEntry.getName()));
56 } 56 }
57 57
58 // add them to this node 58 // add them to this node
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 4b03a332..c96d3bc4 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -114,8 +114,8 @@ public class JarIndex {
114 // step 3: index extends, implements, fields, and methods 114 // step 3: index extends, implements, fields, and methods
115 for (CtClass c : JarClassIterator.classes(jar)) { 115 for (CtClass c : JarClassIterator.classes(jar)) {
116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); 116 ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage);
117 m_translationIndex.indexClass(c);
117 String className = Descriptor.toJvmName(c.getName()); 118 String className = Descriptor.toJvmName(c.getName());
118 m_translationIndex.addSuperclass(className, Descriptor.toJvmName(c.getClassFile().getSuperclass()));
119 for (String interfaceName : c.getClassFile().getInterfaces()) { 119 for (String interfaceName : c.getClassFile().getInterfaces()) {
120 className = Descriptor.toJvmName(className); 120 className = Descriptor.toJvmName(className);
121 interfaceName = Descriptor.toJvmName(interfaceName); 121 interfaceName = Descriptor.toJvmName(interfaceName);
@@ -191,8 +191,6 @@ public class JarIndex {
191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName()); 191 String className = Descriptor.toJvmName(field.getDeclaringClass().getName());
192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName()); 192 FieldEntry fieldEntry = new FieldEntry(new ClassEntry(className), field.getName());
193 193
194 m_translationIndex.addField(className, field.getName());
195
196 // is the field a class type? 194 // is the field a class type?
197 if (field.getSignature().startsWith("L")) { 195 if (field.getSignature().startsWith("L")) {
198 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1)); 196 ClassEntry fieldTypeEntry = new ClassEntry(field.getSignature().substring(1, field.getSignature().length() - 1));
@@ -230,13 +228,12 @@ public class JarIndex {
230 behavior.instrument(new ExprEditor() { 228 behavior.instrument(new ExprEditor() {
231 @Override 229 @Override
232 public void edit(MethodCall call) { 230 public void edit(MethodCall call) {
233 String className = Descriptor.toJvmName(call.getClassName());
234 MethodEntry calledMethodEntry = new MethodEntry( 231 MethodEntry calledMethodEntry = new MethodEntry(
235 new ClassEntry(className), 232 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
236 call.getMethodName(), 233 call.getMethodName(),
237 call.getSignature() 234 call.getSignature()
238 ); 235 );
239 ClassEntry resolvedClassEntry = resolveEntryClass(calledMethodEntry); 236 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledMethodEntry);
240 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) { 237 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledMethodEntry.getClassEntry())) {
241 calledMethodEntry = new MethodEntry( 238 calledMethodEntry = new MethodEntry(
242 resolvedClassEntry, 239 resolvedClassEntry,
@@ -254,12 +251,11 @@ public class JarIndex {
254 251
255 @Override 252 @Override
256 public void edit(FieldAccess call) { 253 public void edit(FieldAccess call) {
257 String className = Descriptor.toJvmName(call.getClassName());
258 FieldEntry calledFieldEntry = new FieldEntry( 254 FieldEntry calledFieldEntry = new FieldEntry(
259 new ClassEntry(className), 255 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
260 call.getFieldName() 256 call.getFieldName()
261 ); 257 );
262 ClassEntry resolvedClassEntry = resolveEntryClass(calledFieldEntry); 258 ClassEntry resolvedClassEntry = m_translationIndex.resolveEntryClass(calledFieldEntry);
263 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) { 259 if (resolvedClassEntry != null && !resolvedClassEntry.equals(calledFieldEntry.getClassEntry())) {
264 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName()); 260 calledFieldEntry = new FieldEntry(resolvedClassEntry, call.getFieldName());
265 } 261 }
@@ -273,9 +269,8 @@ public class JarIndex {
273 269
274 @Override 270 @Override
275 public void edit(ConstructorCall call) { 271 public void edit(ConstructorCall call) {
276 String className = Descriptor.toJvmName(call.getClassName());
277 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 272 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
278 new ClassEntry(className), 273 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
279 call.getSignature() 274 call.getSignature()
280 ); 275 );
281 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 276 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -288,9 +283,8 @@ public class JarIndex {
288 283
289 @Override 284 @Override
290 public void edit(NewExpr call) { 285 public void edit(NewExpr call) {
291 String className = Descriptor.toJvmName(call.getClassName());
292 ConstructorEntry calledConstructorEntry = new ConstructorEntry( 286 ConstructorEntry calledConstructorEntry = new ConstructorEntry(
293 new ClassEntry(className), 287 new ClassEntry(Descriptor.toJvmName(call.getClassName())),
294 call.getSignature() 288 call.getSignature()
295 ); 289 );
296 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>( 290 EntryReference<BehaviorEntry,BehaviorEntry> reference = new EntryReference<BehaviorEntry,BehaviorEntry>(
@@ -306,25 +300,6 @@ public class JarIndex {
306 } 300 }
307 } 301 }
308 302
309 public ClassEntry resolveEntryClass(Entry obfEntry) {
310
311 // this entry could refer to a method on a class where the method is not actually implemented
312 // travel up the inheritance tree to find the closest implementation
313 while (!containsObfEntry(obfEntry)) {
314 // is there a parent class?
315 String superclassName = m_translationIndex.getSuperclassName(obfEntry.getClassName());
316 if (superclassName == null) {
317 // this is probably a method from a class in a library
318 // we can't trace the implementation up any higher unless we index the library
319 return null;
320 }
321
322 // move up to the parent class
323 obfEntry = obfEntry.cloneToNewClass(new ClassEntry(superclassName));
324 }
325 return obfEntry.getClassEntry();
326 }
327
328 private CtMethod getBridgedMethod(CtMethod method) { 303 private CtMethod getBridgedMethod(CtMethod method) {
329 304
330 // bridge methods just call another method, cast it to the return type, and return the result 305 // bridge methods just call another method, cast it to the return type, and return the result
@@ -402,9 +377,9 @@ public class JarIndex {
402 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) { 377 if (reference.entry instanceof ConstructorEntry && reference.context instanceof ConstructorEntry) {
403 378
404 // is the entry a superclass of the context? 379 // is the entry a superclass of the context?
405 String calledClassName = reference.entry.getClassName(); 380 ClassEntry calledClassEntry = reference.entry.getClassEntry();
406 String callerSuperclassName = m_translationIndex.getSuperclassName(reference.context.getClassName()); 381 ClassEntry superclassEntry = m_translationIndex.getSuperclass(reference.context.getClassEntry());
407 if (callerSuperclassName != null && callerSuperclassName.equals(calledClassName)) { 382 if (superclassEntry != null && superclassEntry.equals(calledClassEntry)) {
408 // it's a super call, skip 383 // it's a super call, skip
409 continue; 384 continue;
410 } 385 }
@@ -599,7 +574,9 @@ public class JarIndex {
599 // get the root node 574 // get the root node
600 List<String> ancestry = Lists.newArrayList(); 575 List<String> ancestry = Lists.newArrayList();
601 ancestry.add(obfClassEntry.getName()); 576 ancestry.add(obfClassEntry.getName());
602 ancestry.addAll(m_translationIndex.getAncestry(obfClassEntry.getName())); 577 for (ClassEntry classEntry : m_translationIndex.getAncestry(obfClassEntry)) {
578 ancestry.add(classEntry.getName());
579 }
603 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode( 580 ClassInheritanceTreeNode rootNode = new ClassInheritanceTreeNode(
604 deobfuscatingTranslator, 581 deobfuscatingTranslator,
605 ancestry.get(ancestry.size() - 1) 582 ancestry.get(ancestry.size() - 1)
@@ -625,21 +602,21 @@ public class JarIndex {
625 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) { 602 public MethodInheritanceTreeNode getMethodInheritance(Translator deobfuscatingTranslator, MethodEntry obfMethodEntry) {
626 603
627 // travel to the ancestor implementation 604 // travel to the ancestor implementation
628 String baseImplementationClassName = obfMethodEntry.getClassName(); 605 ClassEntry baseImplementationClassEntry = obfMethodEntry.getClassEntry();
629 for (String ancestorClassName : m_translationIndex.getAncestry(obfMethodEntry.getClassName())) { 606 for (ClassEntry ancestorClassEntry : m_translationIndex.getAncestry(obfMethodEntry.getClassEntry())) {
630 MethodEntry ancestorMethodEntry = new MethodEntry( 607 MethodEntry ancestorMethodEntry = new MethodEntry(
631 new ClassEntry(ancestorClassName), 608 new ClassEntry(ancestorClassEntry),
632 obfMethodEntry.getName(), 609 obfMethodEntry.getName(),
633 obfMethodEntry.getSignature() 610 obfMethodEntry.getSignature()
634 ); 611 );
635 if (containsObfBehavior(ancestorMethodEntry)) { 612 if (containsObfBehavior(ancestorMethodEntry)) {
636 baseImplementationClassName = ancestorClassName; 613 baseImplementationClassEntry = ancestorClassEntry;
637 } 614 }
638 } 615 }
639 616
640 // make a root node at the base 617 // make a root node at the base
641 MethodEntry methodEntry = new MethodEntry( 618 MethodEntry methodEntry = new MethodEntry(
642 new ClassEntry(baseImplementationClassName), 619 baseImplementationClassEntry,
643 obfMethodEntry.getName(), 620 obfMethodEntry.getName(),
644 obfMethodEntry.getSignature() 621 obfMethodEntry.getSignature()
645 ); 622 );
@@ -781,8 +758,8 @@ public class JarIndex {
781 public Set<String> getInterfaces(String className) { 758 public Set<String> getInterfaces(String className) {
782 Set<String> interfaceNames = new HashSet<String>(); 759 Set<String> interfaceNames = new HashSet<String>();
783 interfaceNames.addAll(m_interfaces.get(className)); 760 interfaceNames.addAll(m_interfaces.get(className));
784 for (String ancestor : m_translationIndex.getAncestry(className)) { 761 for (ClassEntry ancestor : m_translationIndex.getAncestry(new ClassEntry(className))) {
785 interfaceNames.addAll(m_interfaces.get(ancestor)); 762 interfaceNames.addAll(m_interfaces.get(ancestor.getName()));
786 } 763 }
787 return interfaceNames; 764 return interfaceNames;
788 } 765 }
@@ -795,7 +772,7 @@ public class JarIndex {
795 String interfaceName = entry.getValue(); 772 String interfaceName = entry.getValue();
796 if (interfaceName.equals(targetInterfaceName)) { 773 if (interfaceName.equals(targetInterfaceName)) {
797 classNames.add(className); 774 classNames.add(className);
798 m_translationIndex.getSubclassNamesRecursively(classNames, className); 775 m_translationIndex.getSubclassNamesRecursively(classNames, new ClassEntry(className));
799 } 776 }
800 } 777 }
801 return classNames; 778 return classNames;
diff --git a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
index eba8d874..87182204 100644
--- a/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
+++ b/src/cuchaz/enigma/analysis/MethodInheritanceTreeNode.java
@@ -71,9 +71,9 @@ public class MethodInheritanceTreeNode extends DefaultMutableTreeNode {
71 public void load(JarIndex index, boolean recurse) { 71 public void load(JarIndex index, boolean recurse) {
72 // get all the child nodes 72 // get all the child nodes
73 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList(); 73 List<MethodInheritanceTreeNode> nodes = Lists.newArrayList();
74 for (String subclassName : index.getTranslationIndex().getSubclassNames(m_entry.getClassName())) { 74 for (ClassEntry subclassEntry : index.getTranslationIndex().getSubclass(m_entry.getClassEntry())) {
75 MethodEntry methodEntry = new MethodEntry( 75 MethodEntry methodEntry = new MethodEntry(
76 new ClassEntry(subclassName), 76 subclassEntry,
77 m_entry.getName(), 77 m_entry.getName(),
78 m_entry.getSignature() 78 m_entry.getSignature()
79 ); 79 );
diff --git a/src/cuchaz/enigma/analysis/TranslationIndex.java b/src/cuchaz/enigma/analysis/TranslationIndex.java
index c14fd593..4a356eb6 100644
--- a/src/cuchaz/enigma/analysis/TranslationIndex.java
+++ b/src/cuchaz/enigma/analysis/TranslationIndex.java
@@ -11,97 +11,185 @@
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.io.Serializable; 13import java.io.Serializable;
14import java.util.ArrayList;
15import java.util.List; 14import java.util.List;
16import java.util.Map; 15import java.util.Map;
17import java.util.Set; 16import java.util.Set;
18 17
19import javassist.bytecode.Descriptor; 18import javassist.CtBehavior;
19import javassist.CtClass;
20import javassist.CtField;
20 21
21import com.google.common.collect.HashMultimap; 22import com.google.common.collect.HashMultimap;
22import com.google.common.collect.Lists; 23import com.google.common.collect.Lists;
23import com.google.common.collect.Maps; 24import com.google.common.collect.Maps;
24import com.google.common.collect.Multimap; 25import com.google.common.collect.Multimap;
25 26
27import cuchaz.enigma.mapping.ArgumentEntry;
28import cuchaz.enigma.mapping.BehaviorEntry;
29import cuchaz.enigma.mapping.ClassEntry;
30import cuchaz.enigma.mapping.Entry;
31import cuchaz.enigma.mapping.FieldEntry;
32import cuchaz.enigma.mapping.JavassistUtil;
33import cuchaz.enigma.mapping.Translator;
34
26public class TranslationIndex implements Serializable { 35public class TranslationIndex implements Serializable {
27 36
28 private static final long serialVersionUID = 738687982126844179L; 37 private static final long serialVersionUID = 738687982126844179L;
29 38
30 private Map<String,String> m_superclasses; 39 private Map<ClassEntry,ClassEntry> m_superclasses;
31 private Multimap<String,String> m_fields; 40 private Multimap<ClassEntry,FieldEntry> m_fieldEntries;
41 private Multimap<ClassEntry,BehaviorEntry> m_behaviorEntries;
32 42
33 public TranslationIndex() { 43 public TranslationIndex() {
34 m_superclasses = Maps.newHashMap(); 44 m_superclasses = Maps.newHashMap();
35 m_fields = HashMultimap.create(); 45 m_fieldEntries = HashMultimap.create();
36 } 46 m_behaviorEntries = HashMultimap.create();
37
38 public TranslationIndex(TranslationIndex other) {
39 m_superclasses = Maps.newHashMap(other.m_superclasses);
40 m_fields = HashMultimap.create(other.m_fields);
41 } 47 }
42 48
43 public void addSuperclass(String className, String superclassName) { 49 public TranslationIndex(TranslationIndex other, Translator translator) {
44 className = Descriptor.toJvmName(className);
45 superclassName = Descriptor.toJvmName(superclassName);
46 50
47 if (className.equals(superclassName)) { 51 // translate the superclasses
48 throw new IllegalArgumentException("Class cannot be its own superclass! " + className); 52 m_superclasses = Maps.newHashMap();
53 for (Map.Entry<ClassEntry,ClassEntry> mapEntry : other.m_superclasses.entrySet()) {
54 m_superclasses.put(
55 translator.translateEntry(mapEntry.getKey()),
56 translator.translateEntry(mapEntry.getValue())
57 );
49 } 58 }
50 59
51 if (!isJre(className) && !isJre(superclassName)) { 60 // translate the fields
52 m_superclasses.put(className, superclassName); 61 m_fieldEntries = HashMultimap.create();
62 for (Map.Entry<ClassEntry,FieldEntry> mapEntry : other.m_fieldEntries.entries()) {
63 m_fieldEntries.put(
64 translator.translateEntry(mapEntry.getKey()),
65 translator.translateEntry(mapEntry.getValue())
66 );
67 }
68
69 m_behaviorEntries = HashMultimap.create();
70 for (Map.Entry<ClassEntry,BehaviorEntry> mapEntry : other.m_behaviorEntries.entries()) {
71 m_behaviorEntries.put(
72 translator.translateEntry(mapEntry.getKey()),
73 translator.translateEntry(mapEntry.getValue())
74 );
53 } 75 }
54 } 76 }
55 77
56 public void addField(String className, String fieldName) { 78 public void indexClass(CtClass c) {
57 m_fields.put(className, fieldName); 79
80 ClassEntry classEntry = JavassistUtil.getClassEntry(c);
81
82 // add the superclass
83 ClassEntry superclassEntry = JavassistUtil.getSuperclassEntry(c);
84 if (!isJre(classEntry) && !isJre(superclassEntry)) {
85 m_superclasses.put(classEntry, superclassEntry);
86 }
87
88 // add fields
89 for (CtField field : c.getDeclaredFields()) {
90 FieldEntry fieldEntry = JavassistUtil.getFieldEntry(field);
91 m_fieldEntries.put(fieldEntry.getClassEntry(), fieldEntry);
92 }
93
94 // add behaviors
95 for (CtBehavior behavior : c.getDeclaredBehaviors()) {
96 BehaviorEntry behaviorEntry = JavassistUtil.getBehaviorEntry(behavior);
97 m_behaviorEntries.put(behaviorEntry.getClassEntry(), behaviorEntry);
98 }
58 } 99 }
59 100
60 public void renameClasses(Map<String,String> renames) { 101 public void renameClasses(Map<String,String> renames) {
61 EntryRenamer.renameClassesInMap(renames, m_superclasses); 102 EntryRenamer.renameClassesInMap(renames, m_superclasses);
62 EntryRenamer.renameClassesInMultimap(renames, m_fields); 103 EntryRenamer.renameClassesInMultimap(renames, m_fieldEntries);
104 EntryRenamer.renameClassesInMultimap(renames, m_behaviorEntries);
63 } 105 }
64 106
65 public String getSuperclassName(String className) { 107 public ClassEntry getSuperclass(ClassEntry classEntry) {
66 return m_superclasses.get(className); 108 return m_superclasses.get(classEntry);
67 } 109 }
68 110
69 public List<String> getAncestry(String className) { 111 public List<ClassEntry> getAncestry(ClassEntry classEntry) {
70 List<String> ancestors = new ArrayList<String>(); 112 List<ClassEntry> ancestors = Lists.newArrayList();
71 while (className != null) { 113 while (classEntry != null) {
72 className = getSuperclassName(className); 114 classEntry = getSuperclass(classEntry);
73 if (className != null) { 115 if (classEntry != null) {
74 ancestors.add(className); 116 ancestors.add(classEntry);
75 } 117 }
76 } 118 }
77 return ancestors; 119 return ancestors;
78 } 120 }
79 121
80 public List<String> getSubclassNames(String className) { 122 public List<ClassEntry> getSubclass(ClassEntry classEntry) {
81 // linear search is fast enough for now 123 // linear search is fast enough for now
82 List<String> subclasses = Lists.newArrayList(); 124 List<ClassEntry> subclasses = Lists.newArrayList();
83 for (Map.Entry<String,String> entry : m_superclasses.entrySet()) { 125 for (Map.Entry<ClassEntry,ClassEntry> entry : m_superclasses.entrySet()) {
84 String subclass = entry.getKey(); 126 ClassEntry subclass = entry.getKey();
85 String superclass = entry.getValue(); 127 ClassEntry superclass = entry.getValue();
86 if (className.equals(superclass)) { 128 if (classEntry.equals(superclass)) {
87 subclasses.add(subclass); 129 subclasses.add(subclass);
88 } 130 }
89 } 131 }
90 return subclasses; 132 return subclasses;
91 } 133 }
92 134
93 public void getSubclassNamesRecursively(Set<String> out, String className) { 135 public void getSubclassesRecursively(Set<ClassEntry> out, ClassEntry classEntry) {
94 for (String subclassName : getSubclassNames(className)) { 136 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
95 out.add(subclassName); 137 out.add(subclassEntry);
96 getSubclassNamesRecursively(out, subclassName); 138 getSubclassesRecursively(out, subclassEntry);
139 }
140 }
141
142 public void getSubclassNamesRecursively(Set<String> out, ClassEntry classEntry) {
143 for (ClassEntry subclassEntry : getSubclass(classEntry)) {
144 out.add(subclassEntry.getName());
145 getSubclassNamesRecursively(out, subclassEntry);
97 } 146 }
98 } 147 }
99 148
100 public boolean containsField(String className, String fieldName) { 149 public boolean entryExists(Entry entry) {
101 return m_fields.containsEntry(className, fieldName); 150 if (entry instanceof FieldEntry) {
151 return fieldExists((FieldEntry)entry);
152 } else if (entry instanceof BehaviorEntry) {
153 return behaviorExists((BehaviorEntry)entry);
154 } else if (entry instanceof ArgumentEntry) {
155 return behaviorExists(((ArgumentEntry)entry).getBehaviorEntry());
156 }
157 throw new IllegalArgumentException("Cannot check existence for " + entry.getClass());
158 }
159
160 public boolean fieldExists(FieldEntry fieldEntry) {
161 return m_fieldEntries.containsEntry(fieldEntry.getClassEntry(), fieldEntry);
162 }
163
164 public boolean behaviorExists(BehaviorEntry behaviorEntry) {
165 return m_behaviorEntries.containsEntry(behaviorEntry.getClassEntry(), behaviorEntry);
166 }
167
168 public ClassEntry resolveEntryClass(Entry entry) {
169
170 if (entry instanceof ClassEntry) {
171 return (ClassEntry)entry;
172 }
173
174 // this entry could refer to a method on a class where the method is not actually implemented
175 // travel up the inheritance tree to find the closest implementation
176 while (!entryExists(entry)) {
177
178 // is there a parent class?
179 ClassEntry superclassEntry = getSuperclass(entry.getClassEntry());
180 if (superclassEntry == null) {
181 // this is probably a method from a class in a library
182 // we can't trace the implementation up any higher unless we index the library
183 return null;
184 }
185
186 // move up to the parent class
187 entry = entry.cloneToNewClass(superclassEntry);
188 }
189 return entry.getClassEntry();
102 } 190 }
103 191
104 private boolean isJre(String className) { 192 private boolean isJre(ClassEntry classEntry) {
105 return className.startsWith("java/") || className.startsWith("javax/"); 193 return classEntry.getPackageName().startsWith("java") || classEntry.getPackageName().startsWith("javax");
106 } 194 }
107} 195}
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java
index 735a8fa3..64418307 100644
--- a/src/cuchaz/enigma/bytecode/ClassTranslator.java
+++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -38,6 +38,7 @@ public class ClassTranslator {
38 } 38 }
39 39
40 public void translate(CtClass c) { 40 public void translate(CtClass c) {
41
41 // NOTE: the order of these translations is very important 42 // NOTE: the order of these translations is very important
42 43
43 // translate all the field and method references in the code by editing the constant pool 44 // translate all the field and method references in the code by editing the constant pool
@@ -45,7 +46,9 @@ public class ClassTranslator {
45 ConstPoolEditor editor = new ConstPoolEditor(constants); 46 ConstPoolEditor editor = new ConstPoolEditor(constants);
46 for (int i = 1; i < constants.getSize(); i++) { 47 for (int i = 1; i < constants.getSize(); i++) {
47 switch (constants.getTag(i)) { 48 switch (constants.getTag(i)) {
49
48 case ConstPool.CONST_Fieldref: { 50 case ConstPool.CONST_Fieldref: {
51
49 // translate the name 52 // translate the name
50 FieldEntry entry = new FieldEntry( 53 FieldEntry entry = new FieldEntry(
51 new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), 54 new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))),
@@ -53,6 +56,11 @@ public class ClassTranslator {
53 ); 56 );
54 FieldEntry translatedEntry = m_translator.translateEntry(entry); 57 FieldEntry translatedEntry = m_translator.translateEntry(entry);
55 58
59 // TEMP
60 if (entry.toString().equals("none/bxq.m")) {
61 System.out.println("FIELD: " + entry + " -> " + translatedEntry);
62 }
63
56 // translate the type 64 // translate the type
57 String type = constants.getFieldrefType(i); 65 String type = constants.getFieldrefType(i);
58 String translatedType = m_translator.translateSignature(type); 66 String translatedType = m_translator.translateSignature(type);
@@ -65,6 +73,7 @@ public class ClassTranslator {
65 73
66 case ConstPool.CONST_Methodref: 74 case ConstPool.CONST_Methodref:
67 case ConstPool.CONST_InterfaceMethodref: { 75 case ConstPool.CONST_InterfaceMethodref: {
76
68 // translate the name and type 77 // translate the name and type
69 BehaviorEntry entry = BehaviorEntryFactory.create( 78 BehaviorEntry entry = BehaviorEntryFactory.create(
70 Descriptor.toJvmName(editor.getMemberrefClassname(i)), 79 Descriptor.toJvmName(editor.getMemberrefClassname(i)),
diff --git a/src/cuchaz/enigma/mapping/JavassistUtil.java b/src/cuchaz/enigma/mapping/JavassistUtil.java
new file mode 100644
index 00000000..b011e0be
--- /dev/null
+++ b/src/cuchaz/enigma/mapping/JavassistUtil.java
@@ -0,0 +1,55 @@
1package cuchaz.enigma.mapping;
2
3import javassist.CtBehavior;
4import javassist.CtClass;
5import javassist.CtConstructor;
6import javassist.CtField;
7import javassist.CtMethod;
8import javassist.bytecode.Descriptor;
9import cuchaz.enigma.mapping.BehaviorEntry;
10import cuchaz.enigma.mapping.ClassEntry;
11import cuchaz.enigma.mapping.ConstructorEntry;
12import cuchaz.enigma.mapping.FieldEntry;
13import cuchaz.enigma.mapping.MethodEntry;
14
15public class JavassistUtil {
16
17 public static ClassEntry getClassEntry(CtClass c) {
18 return new ClassEntry(Descriptor.toJvmName(c.getName()));
19 }
20
21 public static ClassEntry getSuperclassEntry(CtClass c) {
22 return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
23 }
24
25 public static MethodEntry getMethodEntry(CtMethod method) {
26 return new MethodEntry(
27 getClassEntry(method.getDeclaringClass()),
28 method.getName(),
29 method.getMethodInfo().getDescriptor()
30 );
31 }
32
33 public static ConstructorEntry getConstructorEntry(CtConstructor constructor) {
34 return new ConstructorEntry(
35 getClassEntry(constructor.getDeclaringClass()),
36 constructor.getMethodInfo().getDescriptor()
37 );
38 }
39
40 public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) {
41 if (behavior instanceof CtMethod) {
42 return getMethodEntry((CtMethod)behavior);
43 } else if (behavior instanceof CtConstructor) {
44 return getConstructorEntry((CtConstructor)behavior);
45 }
46 throw new Error("behavior is neither Method nor Constructor!");
47 }
48
49 public static FieldEntry getFieldEntry(CtField field) {
50 return new FieldEntry(
51 getClassEntry(field.getDeclaringClass()),
52 field.getName()
53 );
54 }
55}
diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java
index c5e38f4b..cc560a87 100644
--- a/src/cuchaz/enigma/mapping/Mappings.java
+++ b/src/cuchaz/enigma/mapping/Mappings.java
@@ -24,6 +24,7 @@ import com.google.common.collect.Maps;
24import com.google.common.collect.Sets; 24import com.google.common.collect.Sets;
25 25
26import cuchaz.enigma.Util; 26import cuchaz.enigma.Util;
27import cuchaz.enigma.analysis.TranslationIndex;
27import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 28import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
28 29
29public class Mappings implements Serializable { 30public class Mappings implements Serializable {
@@ -104,11 +105,11 @@ public class Mappings implements Serializable {
104 return m_classesByDeobf.get(deobfName); 105 return m_classesByDeobf.get(deobfName);
105 } 106 }
106 107
107 public Translator getTranslator(TranslationDirection direction) { 108 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
108 switch (direction) { 109 switch (direction) {
109 case Deobfuscating: 110 case Deobfuscating:
110 111
111 return new Translator(direction, m_classesByObf); 112 return new Translator(direction, m_classesByObf, index);
112 113
113 case Obfuscating: 114 case Obfuscating:
114 115
@@ -122,7 +123,11 @@ public class Mappings implements Serializable {
122 } 123 }
123 } 124 }
124 125
125 return new Translator(direction, classes); 126 // translate the translation index
127 // NOTE: this isn't actually recursive
128 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index));
129
130 return new Translator(direction, classes, deobfIndex);
126 131
127 default: 132 default:
128 throw new Error("Invalid translation direction!"); 133 throw new Error("Invalid translation direction!");
diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java
index cb95f42b..3aac65a1 100644
--- a/src/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -100,10 +100,10 @@ public class MappingsRenamer {
100 100
101 deobfName = NameValidator.validateMethodName(deobfName); 101 deobfName = NameValidator.validateMethodName(deobfName);
102 for (MethodEntry entry : implementations) { 102 for (MethodEntry entry : implementations) {
103 String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateSignature(obf.getSignature()); 103 String deobfSignature = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateSignature(obf.getSignature());
104 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature); 104 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, deobfSignature);
105 if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) { 105 if (m_mappings.containsDeobfMethod(entry.getClassEntry(), deobfName, entry.getSignature()) || m_index.containsObfBehavior(targetEntry)) {
106 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(entry.getClassName()); 106 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(entry.getClassName());
107 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 107 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
108 } 108 }
109 } 109 }
@@ -117,7 +117,7 @@ public class MappingsRenamer {
117 deobfName = NameValidator.validateMethodName(deobfName); 117 deobfName = NameValidator.validateMethodName(deobfName);
118 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); 118 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature());
119 if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) { 119 if (m_mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) || m_index.containsObfBehavior(targetEntry)) {
120 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating).translateClass(obf.getClassName()); 120 String deobfClassName = m_mappings.getTranslator(TranslationDirection.Deobfuscating, m_index.getTranslationIndex()).translateClass(obf.getClassName());
121 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 121 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
122 } 122 }
123 123
diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java
index d8d9f480..a5a3e2f0 100644
--- a/src/cuchaz/enigma/mapping/Translator.java
+++ b/src/cuchaz/enigma/mapping/Translator.java
@@ -14,21 +14,24 @@ import java.util.Map;
14 14
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16 16
17import cuchaz.enigma.analysis.TranslationIndex;
17import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater; 18import cuchaz.enigma.mapping.SignatureUpdater.ClassNameUpdater;
18 19
19public class Translator { 20public class Translator {
20 21
21 private TranslationDirection m_direction; 22 private TranslationDirection m_direction;
22 private Map<String,ClassMapping> m_classes; 23 private Map<String,ClassMapping> m_classes;
24 private TranslationIndex m_index;
23 25
24 public Translator() { 26 public Translator() {
25 m_direction = null; 27 m_direction = null;
26 m_classes = Maps.newHashMap(); 28 m_classes = Maps.newHashMap();
27 } 29 }
28 30
29 public Translator(TranslationDirection direction, Map<String,ClassMapping> classes) { 31 public Translator(TranslationDirection direction, Map<String,ClassMapping> classes, TranslationIndex index) {
30 m_direction = direction; 32 m_direction = direction;
31 m_classes = classes; 33 m_classes = classes;
34 m_index = index;
32 } 35 }
33 36
34 @SuppressWarnings("unchecked") 37 @SuppressWarnings("unchecked")
@@ -100,17 +103,22 @@ public class Translator {
100 103
101 public String translate(FieldEntry in) { 104 public String translate(FieldEntry in) {
102 105
103 // look for the class 106 // resolve the class entry
104 ClassMapping classMapping = findClassMapping(in.getClassEntry()); 107 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in);
105 if (classMapping != null) { 108 if (resolvedClassEntry != null) {
106 109
107 // look for the field 110 // look for the class
108 String translatedName = m_direction.choose( 111 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
109 classMapping.getDeobfFieldName(in.getName()), 112 if (classMapping != null) {
110 classMapping.getObfFieldName(in.getName()) 113
111 ); 114 // look for the field
112 if (translatedName != null) { 115 String translatedName = m_direction.choose(
113 return translatedName; 116 classMapping.getDeobfFieldName(in.getName()),
117 classMapping.getObfFieldName(in.getName())
118 );
119 if (translatedName != null) {
120 return translatedName;
121 }
114 } 122 }
115 } 123 }
116 return null; 124 return null;
@@ -126,15 +134,22 @@ public class Translator {
126 134
127 public String translate(MethodEntry in) { 135 public String translate(MethodEntry in) {
128 136
129 // look for class 137 // resolve the class entry
130 ClassMapping classMapping = findClassMapping(in.getClassEntry()); 138 ClassEntry resolvedClassEntry = m_index.resolveEntryClass(in);
131 if (classMapping != null) { 139 if (resolvedClassEntry != null) {
132 140
133 // look for the method 141 // look for class
134 MethodMapping methodMapping = m_direction.choose(classMapping.getMethodByObf(in.getName(), in.getSignature()), 142 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
135 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))); 143 if (classMapping != null) {
136 if (methodMapping != null) { 144
137 return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); 145 // look for the method
146 MethodMapping methodMapping = m_direction.choose(
147 classMapping.getMethodByObf(in.getName(), in.getSignature()),
148 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))
149 );
150 if (methodMapping != null) {
151 return m_direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName());
152 }
138 } 153 }
139 } 154 }
140 return null; 155 return null;
diff --git a/ssjb.py b/ssjb.py
index d3e2bb52..7f051cc5 100644
--- a/ssjb.py
+++ b/ssjb.py
@@ -17,29 +17,30 @@ import fnmatch
17tasks = {} 17tasks = {}
18 18
19def registerTask(name, func): 19def registerTask(name, func):
20 tasks[name] = func 20 tasks[name] = func
21 21
22def run(): 22def run():
23 23
24 # get the task name 24 # get the task name
25 taskName = "main" 25 taskName = "main"
26 if len(sys.argv) > 1: 26 if len(sys.argv) > 1:
27 taskName = sys.argv[1] 27 taskName = sys.argv[1]
28 28
29 # find that task 29 # find that task
30 try: 30 try:
31 task = tasks[taskName] 31 task = tasks[taskName]
32 except: 32 except:
33 print "Couldn't find task: %s" % taskName 33 print "Couldn't find task: %s" % taskName
34 return 34 return
35 35
36 # run it! 36 # run it!
37 task() 37 print "Running task: %s" % taskName
38 task()
38 39
39 40
40# set up the default main task 41# set up the default main task
41def mainTask(): 42def mainTask():
42 print "The main task doesn't do anything by default" 43 print "The main task doesn't do anything by default"
43 44
44registerTask("main", mainTask) 45registerTask("main", mainTask)
45 46
@@ -47,117 +48,123 @@ registerTask("main", mainTask)
47# library of useful functions 48# library of useful functions
48 49
49def findFiles(dirSrc, pattern=None): 50def findFiles(dirSrc, pattern=None):
50 out = [] 51 out = []
51 for root, dirs, files in os.walk(dirSrc): 52 for root, dirs, files in os.walk(dirSrc):
52 for file in files: 53 for file in files:
53 path = os.path.join(root, file)[len(dirSrc) + 1:] 54 path = os.path.join(root, file)[len(dirSrc) + 1:]
54 if pattern is None or fnmatch.fnmatch(path, pattern): 55 if pattern is None or fnmatch.fnmatch(path, pattern):
55 out.append(path) 56 out.append(path)
56 return out 57 return out
57 58
58def copyFile(dirDest, pathSrc, renameTo=None): 59def copyFile(dirDest, pathSrc, renameTo=None):
59 (dirParent, filename) = os.path.split(pathSrc) 60 (dirParent, filename) = os.path.split(pathSrc)
60 if renameTo is None: 61 if renameTo is None:
61 renameTo = filename 62 renameTo = filename
62 pathDest = os.path.join(dirDest, renameTo) 63 pathDest = os.path.join(dirDest, renameTo)
63 shutil.copy2(pathSrc, pathDest) 64 shutil.copy2(pathSrc, pathDest)
64 65
65def copyFiles(dirDest, dirSrc, paths): 66def copyFiles(dirDest, dirSrc, paths):
66 for path in paths: 67 for path in paths:
67 pathSrc = os.path.join(dirSrc, path) 68 pathSrc = os.path.join(dirSrc, path)
68 pathDest = os.path.join(dirDest, path) 69 pathDest = os.path.join(dirDest, path)
69 dirParent = os.path.dirname(pathDest) 70 dirParent = os.path.dirname(pathDest)
70 if not os.path.isdir(dirParent): 71 if not os.path.isdir(dirParent):
71 os.makedirs(dirParent) 72 os.makedirs(dirParent)
72 shutil.copy2(pathSrc, pathDest) 73 shutil.copy2(pathSrc, pathDest)
73 74
74def patternStringToRegex(patternString): 75def patternStringToRegex(patternString):
75 76
76 # escape special chars 77 # escape special chars
77 patternString = re.escape(patternString) 78 patternString = re.escape(patternString)
78 79
79 # process ** and * wildcards 80 # process ** and * wildcards
80 replacements = { 81 replacements = {
81 re.escape("**"): ".*", 82 re.escape("**"): ".*",
82 re.escape("*"): "[^" + os.sep + "]+" 83 re.escape("*"): "[^" + os.sep + "]+"
83 } 84 }
84 def getReplacement(match): 85 def getReplacement(match):
85 print "matched", match 86 print "matched", match
86 return "a" 87 return "a"
87 patternString = re.compile("(\\\\\*)+").sub(lambda m: replacements[m.group(0)], patternString) 88 patternString = re.compile("(\\\\\*)+").sub(lambda m: replacements[m.group(0)], patternString)
88 89
89 return re.compile("^" + patternString + "$") 90 return re.compile("^" + patternString + "$")
90 91
91def matchesAnyPath(path, patternStrings): 92def matchesAnyPath(path, patternStrings):
92 for patternString in patternStrings: 93 for patternString in patternStrings:
93 pattern = patternStringToRegex(patternString) 94 pattern = patternStringToRegex(patternString)
94 # TEMP 95 # TEMP
95 print path, pattern.match(path) is not None 96 print path, pattern.match(path) is not None
96 if pattern.match(path) is not None: 97 if pattern.match(path) is not None:
97 return True 98 return True
98 return False 99 return False
99 100
100def delete(path): 101def delete(path):
101 try: 102 try:
102 if os.path.isdir(path): 103 if os.path.isdir(path):
103 shutil.rmtree(path) 104 shutil.rmtree(path)
104 elif os.path.isfile(path): 105 elif os.path.isfile(path):
105 os.remove(path) 106 os.remove(path)
106 except: 107 except:
107 # don't care if it failed 108 # don't care if it failed
108 pass 109 pass
109 110
110def buildManifest(title, version, author, mainClass=None): 111def buildManifest(title, version, author, mainClass=None):
111 manifest = { 112 manifest = {
112 "Title": title, 113 "Title": title,
113 "Version": version, 114 "Version": version,
114 "Created-by": author 115 "Created-by": author
115 } 116 }
116 if mainClass is not None: 117 if mainClass is not None:
117 manifest["Main-Class"] = mainClass 118 manifest["Main-Class"] = mainClass
118 return manifest 119 return manifest
119 120
120def jar(pathOut, dirIn, dirRoot=None, manifest=None): 121def jar(pathOut, dirIn, dirRoot=None, manifest=None):
121 122
122 # build the base args 123 # build the base args
123 if dirRoot is None: 124 if dirRoot is None:
124 dirRoot = dirIn 125 dirRoot = dirIn
125 dirIn = "." 126 dirIn = "."
126 invokeArgs = ["jar"] 127 invokeArgs = ["jar"]
127 filesArgs = ["-C", dirRoot, dirIn] 128 filesArgs = ["-C", dirRoot, dirIn]
128 129
129 if manifest is not None: 130 if manifest is not None:
130 # make a temp file for the manifest 131 # make a temp file for the manifest
131 tempFile, tempFilename = tempfile.mkstemp(text=True) 132 tempFile, tempFilename = tempfile.mkstemp(text=True)
132 try: 133 try:
133 # write the manifest 134 # write the manifest
134 for (key, value) in manifest.iteritems(): 135 for (key, value) in manifest.iteritems():
135 os.write(tempFile, "%s: %s\n" % (key, value)) 136 os.write(tempFile, "%s: %s\n" % (key, value))
136 os.close(tempFile) 137 os.close(tempFile)
137 138
138 # build the jar with a manifest 139 # build the jar with a manifest
139 subprocess.call(invokeArgs + ["cmf", tempFilename, pathOut] + filesArgs) 140 subprocess.call(invokeArgs + ["cmf", tempFilename, pathOut] + filesArgs)
140 141
141 finally: 142 finally:
142 os.remove(tempFilename) 143 os.remove(tempFilename)
143 else: 144 else:
144 # just build the jar without a manifest 145 # just build the jar without a manifest
145 subprocess.call(invokeArgs + ["cf", pathOut] + filesArgs) 146 subprocess.call(invokeArgs + ["cf", pathOut] + filesArgs)
146 147
147 print "Wrote jar: %s" % pathOut 148 print "Wrote jar: %s" % pathOut
148 149
149def unpackJar(dirOut, pathJar): 150def unpackJar(dirOut, pathJar):
150 with zipfile.ZipFile(pathJar) as zf: 151 with zipfile.ZipFile(pathJar) as zf:
151 for member in zf.infolist(): 152 for member in zf.infolist():
152 zf.extract(member, dirOut) 153 zf.extract(member, dirOut)
153 print "Unpacked jar: %s" % pathJar 154 print "Unpacked jar: %s" % pathJar
154 155
155def unpackJars(dirOut, dirJars, recursive=False): 156def unpackJars(dirOut, dirJars, recursive=False):
156 for name in os.listdir(dirJars): 157 for name in os.listdir(dirJars):
157 path = os.path.join(dirJars, name) 158 path = os.path.join(dirJars, name)
158 if os.path.isfile(path): 159 if os.path.isfile(path):
159 if name[-4:] == ".jar": 160 if name[-4:] == ".jar":
160 unpackJar(dirOut, path) 161 unpackJar(dirOut, path)
161 elif os.path.isdir(path) and recursive: 162 elif os.path.isdir(path) and recursive:
162 unpackJars(dirOut, path, recursive) 163 unpackJars(dirOut, path, recursive)
164
165def callJava(classpath, className, javaArgs):
166 subprocess.call(["java", "-cp", classpath, className] + javaArgs)
167
168def callJavaJar(jar, javaArgs):
169 subprocess.call(["java", "-jar", jar] + javaArgs)
163 170
diff --git a/test/cuchaz/enigma/TestDeobfuscator.java b/test/cuchaz/enigma/TestDeobfuscator.java
index 45d27c40..6ac4c786 100644
--- a/test/cuchaz/enigma/TestDeobfuscator.java
+++ b/test/cuchaz/enigma/TestDeobfuscator.java
@@ -26,7 +26,7 @@ import cuchaz.enigma.mapping.ClassEntry;
26public class TestDeobfuscator { 26public class TestDeobfuscator {
27 27
28 private Deobfuscator getDeobfuscator() throws IOException { 28 private Deobfuscator getDeobfuscator() throws IOException {
29 return new Deobfuscator(new File("build/libs/testLoneClass.obf.jar")); 29 return new Deobfuscator(new File("build/testLoneClass.obf.jar"));
30 } 30 }
31 31
32 @Test 32 @Test
diff --git a/test/cuchaz/enigma/TestInnerClasses.java b/test/cuchaz/enigma/TestInnerClasses.java
index c84d755e..51fb5e35 100644
--- a/test/cuchaz/enigma/TestInnerClasses.java
+++ b/test/cuchaz/enigma/TestInnerClasses.java
@@ -35,7 +35,7 @@ public class TestInnerClasses {
35 35
36 public TestInnerClasses() throws Exception { 36 public TestInnerClasses() throws Exception {
37 m_index = new JarIndex(); 37 m_index = new JarIndex();
38 m_index.indexJar(new JarFile("build/libs/testInnerClasses.obf.jar"), true); 38 m_index.indexJar(new JarFile("build/testInnerClasses.obf.jar"), true);
39 } 39 }
40 40
41 @Test 41 @Test
diff --git a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java
index b5f4c7f3..8e3ad6d2 100644
--- a/test/cuchaz/enigma/TestJarIndexConstructorReferences.java
+++ b/test/cuchaz/enigma/TestJarIndexConstructorReferences.java
@@ -37,7 +37,7 @@ public class TestJarIndexConstructorReferences {
37 private ClassEntry m_callerClass = new ClassEntry("none/b"); 37 private ClassEntry m_callerClass = new ClassEntry("none/b");
38 38
39 public TestJarIndexConstructorReferences() throws Exception { 39 public TestJarIndexConstructorReferences() throws Exception {
40 File jarFile = new File("build/libs/testConstructors.obf.jar"); 40 File jarFile = new File("build/testConstructors.obf.jar");
41 m_index = new JarIndex(); 41 m_index = new JarIndex();
42 m_index.indexJar(new JarFile(jarFile), false); 42 m_index.indexJar(new JarFile(jarFile), false);
43 } 43 }
diff --git a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java
index caf6578d..4d663972 100644
--- a/test/cuchaz/enigma/TestJarIndexInheritanceTree.java
+++ b/test/cuchaz/enigma/TestJarIndexInheritanceTree.java
@@ -43,7 +43,7 @@ public class TestJarIndexInheritanceTree {
43 43
44 public TestJarIndexInheritanceTree() throws Exception { 44 public TestJarIndexInheritanceTree() throws Exception {
45 m_index = new JarIndex(); 45 m_index = new JarIndex();
46 m_index.indexJar(new JarFile("build/libs/testInheritanceTree.obf.jar"), false); 46 m_index.indexJar(new JarFile("build/testInheritanceTree.obf.jar"), false);
47 } 47 }
48 48
49 @Test 49 @Test
@@ -63,27 +63,27 @@ public class TestJarIndexInheritanceTree {
63 TranslationIndex index = m_index.getTranslationIndex(); 63 TranslationIndex index = m_index.getTranslationIndex();
64 64
65 // base class 65 // base class
66 assertThat(index.getSuperclassName(m_baseClass.getName()), is(nullValue())); 66 assertThat(index.getSuperclass(m_baseClass), is(nullValue()));
67 assertThat(index.getAncestry(m_baseClass.getName()), is(empty())); 67 assertThat(index.getAncestry(m_baseClass), is(empty()));
68 assertThat(index.getSubclassNames(m_baseClass.getName()), containsInAnyOrder( 68 assertThat(index.getSubclass(m_baseClass), containsInAnyOrder(
69 m_subClassA.getName(), 69 m_subClassA,
70 m_subClassB.getName() 70 m_subClassB
71 )); 71 ));
72 72
73 // subclass a 73 // subclass a
74 assertThat(index.getSuperclassName(m_subClassA.getName()), is(m_baseClass.getName())); 74 assertThat(index.getSuperclass(m_subClassA), is(m_baseClass));
75 assertThat(index.getAncestry(m_subClassA.getName()), contains(m_baseClass.getName())); 75 assertThat(index.getAncestry(m_subClassA), contains(m_baseClass));
76 assertThat(index.getSubclassNames(m_subClassA.getName()), contains(m_subClassAA.getName())); 76 assertThat(index.getSubclass(m_subClassA), contains(m_subClassAA));
77 77
78 // subclass aa 78 // subclass aa
79 assertThat(index.getSuperclassName(m_subClassAA.getName()), is(m_subClassA.getName())); 79 assertThat(index.getSuperclass(m_subClassAA), is(m_subClassA));
80 assertThat(index.getAncestry(m_subClassAA.getName()), contains(m_subClassA.getName(), m_baseClass.getName())); 80 assertThat(index.getAncestry(m_subClassAA), contains(m_subClassA, m_baseClass));
81 assertThat(index.getSubclassNames(m_subClassAA.getName()), is(empty())); 81 assertThat(index.getSubclass(m_subClassAA), is(empty()));
82 82
83 // subclass b 83 // subclass b
84 assertThat(index.getSuperclassName(m_subClassB.getName()), is(m_baseClass.getName())); 84 assertThat(index.getSuperclass(m_subClassB), is(m_baseClass));
85 assertThat(index.getAncestry(m_subClassB.getName()), contains(m_baseClass.getName())); 85 assertThat(index.getAncestry(m_subClassB), contains(m_baseClass));
86 assertThat(index.getSubclassNames(m_subClassB.getName()), is(empty())); 86 assertThat(index.getSubclass(m_subClassB), is(empty()));
87 } 87 }
88 88
89 @Test 89 @Test
diff --git a/test/cuchaz/enigma/TestJarIndexLoneClass.java b/test/cuchaz/enigma/TestJarIndexLoneClass.java
index 0575eec3..a061b72d 100644
--- a/test/cuchaz/enigma/TestJarIndexLoneClass.java
+++ b/test/cuchaz/enigma/TestJarIndexLoneClass.java
@@ -29,6 +29,7 @@ import cuchaz.enigma.analysis.JarIndex;
29import cuchaz.enigma.analysis.MethodImplementationsTreeNode; 29import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
30import cuchaz.enigma.analysis.MethodInheritanceTreeNode; 30import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
31import cuchaz.enigma.mapping.BehaviorEntry; 31import cuchaz.enigma.mapping.BehaviorEntry;
32import cuchaz.enigma.mapping.ClassEntry;
32import cuchaz.enigma.mapping.FieldEntry; 33import cuchaz.enigma.mapping.FieldEntry;
33import cuchaz.enigma.mapping.MethodEntry; 34import cuchaz.enigma.mapping.MethodEntry;
34import cuchaz.enigma.mapping.Translator; 35import cuchaz.enigma.mapping.Translator;
@@ -39,7 +40,7 @@ public class TestJarIndexLoneClass {
39 40
40 public TestJarIndexLoneClass() throws Exception { 41 public TestJarIndexLoneClass() throws Exception {
41 m_index = new JarIndex(); 42 m_index = new JarIndex();
42 m_index.indexJar(new JarFile("build/libs/testLoneClass.obf.jar"), false); 43 m_index.indexJar(new JarFile("build/testLoneClass.obf.jar"), false);
43 } 44 }
44 45
45 @Test 46 @Test
@@ -52,12 +53,12 @@ public class TestJarIndexLoneClass {
52 53
53 @Test 54 @Test
54 public void translationIndex() { 55 public void translationIndex() {
55 assertThat(m_index.getTranslationIndex().getSuperclassName("none/a"), is(nullValue())); 56 assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("none/a")), is(nullValue()));
56 assertThat(m_index.getTranslationIndex().getSuperclassName("cuchaz/enigma/inputs/Keep"), is(nullValue())); 57 assertThat(m_index.getTranslationIndex().getSuperclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(nullValue()));
57 assertThat(m_index.getTranslationIndex().getAncestry("none/a"), is(empty())); 58 assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("none/a")), is(empty()));
58 assertThat(m_index.getTranslationIndex().getAncestry("cuchaz/enigma/inputs/Keep"), is(empty())); 59 assertThat(m_index.getTranslationIndex().getAncestry(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty()));
59 assertThat(m_index.getTranslationIndex().getSubclassNames("none/a"), is(empty())); 60 assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("none/a")), is(empty()));
60 assertThat(m_index.getTranslationIndex().getSubclassNames("cuchaz/enigma/inputs/Keep"), is(empty())); 61 assertThat(m_index.getTranslationIndex().getSubclass(new ClassEntry("cuchaz/enigma/inputs/Keep")), is(empty()));
61 } 62 }
62 63
63 @Test 64 @Test
diff --git a/test/cuchaz/enigma/TestSourceIndex.java b/test/cuchaz/enigma/TestSourceIndex.java
index fb385e06..ba7fc797 100644
--- a/test/cuchaz/enigma/TestSourceIndex.java
+++ b/test/cuchaz/enigma/TestSourceIndex.java
@@ -23,6 +23,7 @@ import cuchaz.enigma.mapping.ClassEntry;
23 23
24public class TestSourceIndex { 24public class TestSourceIndex {
25 25
26 // TEMP
26 @Test 27 @Test
27 public void indexEverything() throws Exception { 28 public void indexEverything() throws Exception {
28 Deobfuscator deobfuscator = new Deobfuscator(new File("input/1.8.jar")); 29 Deobfuscator deobfuscator = new Deobfuscator(new File("input/1.8.jar"));
diff --git a/test/cuchaz/enigma/TestTokensConstructors.java b/test/cuchaz/enigma/TestTokensConstructors.java
index f805a655..9f85e81d 100644
--- a/test/cuchaz/enigma/TestTokensConstructors.java
+++ b/test/cuchaz/enigma/TestTokensConstructors.java
@@ -27,7 +27,7 @@ import cuchaz.enigma.mapping.BehaviorEntry;
27public class TestTokensConstructors extends TokenChecker { 27public class TestTokensConstructors extends TokenChecker {
28 28
29 public TestTokensConstructors() throws Exception { 29 public TestTokensConstructors() throws Exception {
30 super(new File("build/libs/testConstructors.obf.jar")); 30 super(new File("build/testConstructors.obf.jar"));
31 } 31 }
32 32
33 @Test 33 @Test