summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/mapping
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/cuchaz/enigma/mapping')
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java110
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java (renamed from src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java)17
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassEntry.java26
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java163
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java105
-rw-r--r--src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java319
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Entry.java4
-rw-r--r--src/main/java/cuchaz/enigma/mapping/EntryFactory.java101
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java34
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldEntry.java51
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java36
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java75
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java65
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java (renamed from src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java)12
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java45
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java10
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java18
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java163
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java6
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java4
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java35
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java113
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodEntry.java59
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java130
-rw-r--r--src/main/java/cuchaz/enigma/mapping/NameValidator.java15
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java36
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java50
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Signature.java106
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TranslationDirection.java10
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java344
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java240
33 files changed, 1352 insertions, 1182 deletions
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java b/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
deleted file mode 100644
index 9154cc2..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java
+++ /dev/null
@@ -1,110 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.utils.Utils;
15
16public class ArgumentEntry implements Entry {
17
18 private BehaviorEntry behaviorEntry;
19 private int index;
20 private String name;
21
22 public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) {
23 if (behaviorEntry == null) {
24 throw new IllegalArgumentException("Behavior cannot be null!");
25 }
26 if (index < 0) {
27 throw new IllegalArgumentException("Index must be non-negative!");
28 }
29 if (name == null) {
30 throw new IllegalArgumentException("Argument name cannot be null!");
31 }
32
33 this.behaviorEntry = behaviorEntry;
34 this.index = index;
35 this.name = name;
36 }
37
38 public ArgumentEntry(ArgumentEntry other) {
39 this.behaviorEntry = other.getBehaviorEntry();
40 this.index = other.index;
41 this.name = other.name;
42 }
43
44 public ArgumentEntry(ArgumentEntry other, String newClassName) {
45 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName));
46 this.index = other.index;
47 this.name = other.name;
48 }
49
50 public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) {
51 this.behaviorEntry = entry;
52 this.index = other.index;
53 this.name = other.name;
54 }
55
56 public BehaviorEntry getBehaviorEntry() {
57 return this.behaviorEntry;
58 }
59
60 public int getIndex() {
61 return this.index;
62 }
63
64 @Override
65 public String getName() {
66 return this.name;
67 }
68
69 @Override
70 public ClassEntry getClassEntry() {
71 return this.behaviorEntry.getClassEntry();
72 }
73
74 @Override
75 public String getClassName() {
76 return this.behaviorEntry.getClassName();
77 }
78
79 @Override
80 public ArgumentEntry cloneToNewClass(ClassEntry classEntry) {
81 return new ArgumentEntry(this, classEntry.getName());
82 }
83
84 public String getMethodName() {
85 return this.behaviorEntry.getName();
86 }
87
88 public Signature getMethodSignature() {
89 return this.behaviorEntry.getSignature();
90 }
91
92 @Override
93 public int hashCode() {
94 return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode());
95 }
96
97 @Override
98 public boolean equals(Object other) {
99 return other instanceof ArgumentEntry && equals((ArgumentEntry) other);
100 }
101
102 public boolean equals(ArgumentEntry other) {
103 return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name);
104 }
105
106 @Override
107 public String toString() {
108 return this.behaviorEntry + "(" + this.index + ":" + this.name + ")";
109 }
110}
diff --git a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java
index 04b4ebc..dc1b02e 100644
--- a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java
@@ -11,6 +11,19 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14public interface BehaviorEntry extends Entry { 14import com.google.common.base.Preconditions;
15 Signature getSignature(); 15import cuchaz.enigma.bytecode.AccessFlags;
16
17public class ClassDefEntry extends ClassEntry {
18 private final AccessFlags access;
19
20 public ClassDefEntry(String className, AccessFlags access) {
21 super(className);
22 Preconditions.checkNotNull(access, "Class access cannot be null");
23 this.access = access;
24 }
25
26 public AccessFlags getAccess() {
27 return access;
28 }
16} 29}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
index 788811f..a49f8dd 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
@@ -11,18 +11,18 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
15 16
16import java.util.List; 17import java.util.List;
17 18
18public class ClassEntry implements Entry { 19public class ClassEntry implements Entry {
19 20
20 private String name; 21 private final String name;
21 22
22 public ClassEntry(String className) { 23 public ClassEntry(String className) {
23 if (className == null) { 24 Preconditions.checkNotNull(className, "Class name cannot be null");
24 throw new IllegalArgumentException("Class name cannot be null!"); 25
25 }
26 if (className.indexOf('.') >= 0) { 26 if (className.indexOf('.') >= 0) {
27 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); 27 throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className);
28 } 28 }
@@ -49,12 +49,12 @@ public class ClassEntry implements Entry {
49 } 49 }
50 50
51 @Override 51 @Override
52 public ClassEntry getClassEntry() { 52 public ClassEntry getOwnerClassEntry() {
53 return this; 53 return this;
54 } 54 }
55 55
56 @Override 56 @Override
57 public ClassEntry cloneToNewClass(ClassEntry classEntry) { 57 public ClassEntry updateOwnership(ClassEntry classEntry) {
58 return classEntry; 58 return classEntry;
59 } 59 }
60 60
@@ -132,11 +132,7 @@ public class ClassEntry implements Entry {
132 } 132 }
133 133
134 public String getPackageName() { 134 public String getPackageName() {
135 int pos = this.name.lastIndexOf('/'); 135 return getPackageName(this.name);
136 if (pos > 0) {
137 return this.name.substring(0, pos);
138 }
139 return null;
140 } 136 }
141 137
142 public String getSimpleName() { 138 public String getSimpleName() {
@@ -147,6 +143,14 @@ public class ClassEntry implements Entry {
147 return this.name; 143 return this.name;
148 } 144 }
149 145
146 public static String getPackageName(String name) {
147 int pos = name.lastIndexOf('/');
148 if (pos > 0) {
149 return name.substring(0, pos);
150 }
151 return null;
152 }
153
150 public ClassEntry buildClassEntry(List<ClassEntry> classChain) { 154 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
151 assert (classChain.contains(this)); 155 assert (classChain.contains(this));
152 StringBuilder buf = new StringBuilder(); 156 StringBuilder buf = new StringBuilder();
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
index 51751ca..c782250 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
@@ -34,7 +34,6 @@ public class ClassMapping implements Comparable<ClassMapping> {
34 private Map<String, MethodMapping> methodsByDeobf; 34 private Map<String, MethodMapping> methodsByDeobf;
35 private boolean isDirty; 35 private boolean isDirty;
36 private Mappings.EntryModifier modifier; 36 private Mappings.EntryModifier modifier;
37 private boolean deobfInner;
38 37
39 public ClassMapping(String obfFullName) { 38 public ClassMapping(String obfFullName) {
40 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); 39 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
@@ -81,6 +80,10 @@ public class ClassMapping implements Comparable<ClassMapping> {
81 return deobfName; 80 return deobfName;
82 } 81 }
83 82
83 public String getTranslatedName(TranslationDirection direction) {
84 return direction.choose(deobfName, obfFullName);
85 }
86
84 //// INNER CLASSES //////// 87 //// INNER CLASSES ////////
85 88
86 public void setDeobfName(String val) { 89 public void setDeobfName(String val) {
@@ -191,21 +194,21 @@ public class ClassMapping implements Comparable<ClassMapping> {
191 return fieldsByObf.values(); 194 return fieldsByObf.values();
192 } 195 }
193 196
194 public boolean containsObfField(String obfName, Type obfType) { 197 public boolean containsObfField(String obfName, TypeDescriptor obfDesc) {
195 return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); 198 return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc));
196 } 199 }
197 200
198 public boolean containsDeobfField(String deobfName, Type deobfType) { 201 public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) {
199 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); 202 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc));
200 } 203 }
201 204
202 public void addFieldMapping(FieldMapping fieldMapping) { 205 public void addFieldMapping(FieldMapping fieldMapping) {
203 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 206 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
204 if (fieldsByObf.containsKey(obfKey)) { 207 if (fieldsByObf.containsKey(obfKey)) {
205 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 208 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
206 } 209 }
207 if (fieldMapping.getDeobfName() != null) { 210 if (fieldMapping.getDeobfName() != null) {
208 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); 211 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc());
209 if (fieldsByDeobf.containsKey(deobfKey)) { 212 if (fieldsByDeobf.containsKey(deobfKey)) {
210 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 213 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
211 } 214 }
@@ -218,63 +221,67 @@ public class ClassMapping implements Comparable<ClassMapping> {
218 } 221 }
219 222
220 public void removeFieldMapping(FieldMapping fieldMapping) { 223 public void removeFieldMapping(FieldMapping fieldMapping) {
221 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; 224 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null;
222 assert (obfWasRemoved); 225 assert (obfWasRemoved);
223 if (fieldMapping.getDeobfName() != null) { 226 if (fieldMapping.getDeobfName() != null) {
224 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; 227 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null;
225 assert (deobfWasRemoved); 228 assert (deobfWasRemoved);
226 } 229 }
227 this.isDirty = true; 230 this.isDirty = true;
228 } 231 }
229 232
230 public FieldMapping getFieldByObf(String obfName, Type obfType) { 233 public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) {
231 return fieldsByObf.get(getFieldKey(obfName, obfType)); 234 return fieldsByObf.get(getFieldKey(obfName, obfDesc));
235 }
236
237 public FieldMapping getFieldByObf(FieldEntry field) {
238 return getFieldByObf(field.getName(), field.getDesc());
232 } 239 }
233 240
234 public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { 241 public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) {
235 return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 242 return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
236 } 243 }
237 244
238 public String getObfFieldName(String deobfName, Type obfType) { 245 public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) {
239 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 246 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
240 if (fieldMapping != null) { 247 if (fieldMapping != null) {
241 return fieldMapping.getObfName(); 248 return fieldMapping.getObfName();
242 } 249 }
243 return null; 250 return null;
244 } 251 }
245 252
246 public String getDeobfFieldName(String obfName, Type obfType) { 253 public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) {
247 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 254 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
248 if (fieldMapping != null) { 255 if (fieldMapping != null) {
249 return fieldMapping.getDeobfName(); 256 return fieldMapping.getDeobfName();
250 } 257 }
251 return null; 258 return null;
252 } 259 }
253 260
254 private String getFieldKey(String name, Type type) { 261 private String getFieldKey(String name, TypeDescriptor desc) {
255 if (name == null) { 262 if (name == null) {
256 throw new IllegalArgumentException("name cannot be null!"); 263 throw new IllegalArgumentException("name cannot be null!");
257 } 264 }
258 if (type == null) { 265 if (desc == null) {
259 throw new IllegalArgumentException("type cannot be null!"); 266 throw new IllegalArgumentException("desc cannot be null!");
260 } 267 }
261 return name + ":" + type; 268 return name + ":" + desc;
262 } 269 }
263 270
264 public void setFieldName(String obfName, Type obfType, String deobfName) { 271 public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) {
265 assert (deobfName != null); 272 assert (deobfName != null);
266 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 273 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
267 if (fieldMapping == null) { 274 if (fieldMapping == null) {
268 fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); 275 fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED);
269 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; 276 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null;
270 assert (obfWasAdded); 277 assert (obfWasAdded);
271 } else { 278 } else {
272 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; 279 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null;
273 assert (wasRemoved); 280 assert (wasRemoved);
274 } 281 }
275 fieldMapping.setDeobfName(deobfName); 282 fieldMapping.setDeobfName(deobfName);
276 if (deobfName != null) { 283 if (deobfName != null) {
277 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; 284 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null;
278 assert (wasAdded); 285 assert (wasAdded);
279 } 286 }
280 this.isDirty = true; 287 this.isDirty = true;
@@ -282,13 +289,13 @@ public class ClassMapping implements Comparable<ClassMapping> {
282 289
283 //// METHODS //////// 290 //// METHODS ////////
284 291
285 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { 292 public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) {
286 assert (newObfName != null); 293 assert (newObfName != null);
287 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); 294 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc));
288 assert (fieldMapping != null); 295 assert (fieldMapping != null);
289 fieldMapping.setObfName(newObfName); 296 fieldMapping.setObfName(newObfName);
290 fieldMapping.setObfType(newObfType); 297 fieldMapping.setObfDesc(newObfDesc);
291 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; 298 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null;
292 assert (obfWasAdded); 299 assert (obfWasAdded);
293 this.isDirty = true; 300 this.isDirty = true;
294 } 301 }
@@ -298,23 +305,23 @@ public class ClassMapping implements Comparable<ClassMapping> {
298 return methodsByObf.values(); 305 return methodsByObf.values();
299 } 306 }
300 307
301 public boolean containsObfMethod(String obfName, Signature obfSignature) { 308 public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) {
302 return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); 309 return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor));
303 } 310 }
304 311
305 public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { 312 public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) {
306 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); 313 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor));
307 } 314 }
308 315
309 public void addMethodMapping(MethodMapping methodMapping) { 316 public void addMethodMapping(MethodMapping methodMapping) {
310 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 317 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
311 if (methodsByObf.containsKey(obfKey)) { 318 if (methodsByObf.containsKey(obfKey)) {
312 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 319 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
313 } 320 }
314 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; 321 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null;
315 assert (wasAdded); 322 assert (wasAdded);
316 if (methodMapping.getDeobfName() != null) { 323 if (methodMapping.getDeobfName() != null) {
317 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); 324 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc());
318 if (methodsByDeobf.containsKey(deobfKey)) { 325 if (methodsByDeobf.containsKey(deobfKey)) {
319 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 326 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
320 } 327 }
@@ -326,44 +333,48 @@ public class ClassMapping implements Comparable<ClassMapping> {
326 } 333 }
327 334
328 public void removeMethodMapping(MethodMapping methodMapping) { 335 public void removeMethodMapping(MethodMapping methodMapping) {
329 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; 336 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null;
330 assert (obfWasRemoved); 337 assert (obfWasRemoved);
331 if (methodMapping.getDeobfName() != null) { 338 if (methodMapping.getDeobfName() != null) {
332 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 339 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
333 assert (deobfWasRemoved); 340 assert (deobfWasRemoved);
334 } 341 }
335 this.isDirty = true; 342 this.isDirty = true;
336 } 343 }
337 344
338 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { 345 public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) {
339 return methodsByObf.get(getMethodKey(obfName, obfSignature)); 346 return methodsByObf.get(getMethodKey(obfName, obfDescriptor));
347 }
348
349 public MethodMapping getMethodByObf(MethodEntry method) {
350 return getMethodByObf(method.getName(), method.getDesc());
340 } 351 }
341 352
342 public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { 353 public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) {
343 return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); 354 return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor));
344 } 355 }
345 356
346 private String getMethodKey(String name, Signature signature) { 357 private String getMethodKey(String name, MethodDescriptor descriptor) {
347 if (name == null) { 358 if (name == null) {
348 throw new IllegalArgumentException("name cannot be null!"); 359 throw new IllegalArgumentException("name cannot be null!");
349 } 360 }
350 if (signature == null) { 361 if (descriptor == null) {
351 throw new IllegalArgumentException("signature cannot be null!"); 362 throw new IllegalArgumentException("descriptor cannot be null!");
352 } 363 }
353 return name + signature; 364 return name + descriptor;
354 } 365 }
355 366
356 public void setMethodName(String obfName, Signature obfSignature, String deobfName) { 367 public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
357 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); 368 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor));
358 if (methodMapping == null) { 369 if (methodMapping == null) {
359 methodMapping = createMethodMapping(obfName, obfSignature); 370 methodMapping = createMethodMapping(obfName, obfDescriptor);
360 } else if (methodMapping.getDeobfName() != null) { 371 } else if (methodMapping.getDeobfName() != null) {
361 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 372 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
362 assert (wasRemoved); 373 assert (wasRemoved);
363 } 374 }
364 methodMapping.setDeobfName(deobfName); 375 methodMapping.setDeobfName(deobfName);
365 if (deobfName != null) { 376 if (deobfName != null) {
366 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; 377 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null;
367 assert (wasAdded); 378 assert (wasAdded);
368 } 379 }
369 this.isDirty = true; 380 this.isDirty = true;
@@ -371,35 +382,35 @@ public class ClassMapping implements Comparable<ClassMapping> {
371 382
372 //// ARGUMENTS //////// 383 //// ARGUMENTS ////////
373 384
374 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { 385 public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) {
375 assert (newObfName != null); 386 assert (newObfName != null);
376 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); 387 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor));
377 assert (methodMapping != null); 388 assert (methodMapping != null);
378 methodMapping.setObfName(newObfName); 389 methodMapping.setObfName(newObfName);
379 methodMapping.setObfSignature(newObfSignature); 390 methodMapping.setObfDescriptor(newObfDescriptor);
380 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; 391 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null;
381 assert (obfWasAdded); 392 assert (obfWasAdded);
382 this.isDirty = true; 393 this.isDirty = true;
383 } 394 }
384 395
385 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { 396 public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) {
386 assert (argumentName != null); 397 assert (argumentName != null);
387 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); 398 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor));
388 if (methodMapping == null) { 399 if (methodMapping == null) {
389 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); 400 methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor);
390 } 401 }
391 methodMapping.setArgumentName(argumentIndex, argumentName); 402 methodMapping.setLocalVariableName(argumentIndex, argumentName);
392 this.isDirty = true; 403 this.isDirty = true;
393 } 404 }
394 405
395 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { 406 public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) {
396 methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); 407 methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex);
397 this.isDirty = true; 408 this.isDirty = true;
398 } 409 }
399 410
400 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { 411 private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) {
401 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); 412 MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor);
402 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; 413 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null;
403 assert (wasAdded); 414 assert (wasAdded);
404 this.isDirty = true; 415 this.isDirty = true;
405 return methodMapping; 416 return methodMapping;
@@ -459,24 +470,24 @@ public class ClassMapping implements Comparable<ClassMapping> {
459 470
460 // rename field types 471 // rename field types
461 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { 472 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) {
462 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 473 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
463 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { 474 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) {
464 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; 475 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null;
465 assert (wasRemoved); 476 assert (wasRemoved);
466 boolean wasAdded = fieldsByObf 477 boolean wasAdded = fieldsByObf
467 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; 478 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null;
468 assert (wasAdded); 479 assert (wasAdded);
469 } 480 }
470 } 481 }
471 482
472 // rename method signatures 483 // rename method signatures
473 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { 484 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) {
474 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 485 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
475 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { 486 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
476 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; 487 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null;
477 assert (wasRemoved); 488 assert (wasRemoved);
478 boolean wasAdded = methodsByObf 489 boolean wasAdded = methodsByObf
479 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; 490 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null;
480 assert (wasAdded); 491 assert (wasAdded);
481 } 492 }
482 } 493 }
@@ -490,9 +501,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
490 return false; 501 return false;
491 } 502 }
492 503
493 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 504 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
494 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); 505 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc()));
495 return methodMapping != null && methodMapping.containsArgument(name); 506 return methodMapping != null && methodMapping.containsLocalVariable(name);
496 } 507 }
497 508
498 public ClassEntry getObfEntry() { 509 public ClassEntry getObfEntry() {
@@ -521,9 +532,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
521 this.modifier = modifier; 532 this.modifier = modifier;
522 } 533 }
523 534
524 public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { 535 public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) {
525 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), 536 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc),
526 k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); 537 k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED));
527 538
528 if (fieldMapping.getModifier() != modifier) { 539 if (fieldMapping.getModifier() != modifier) {
529 fieldMapping.setModifier(modifier); 540 fieldMapping.setModifier(modifier);
@@ -531,7 +542,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
531 } 542 }
532 } 543 }
533 544
534 public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { 545 public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) {
535 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), 546 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
536 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); 547 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
537 548
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java b/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java
deleted file mode 100644
index 801c410..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java
+++ /dev/null
@@ -1,16 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14public interface ClassNameReplacer {
15 String replace(String className);
16}
diff --git a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java b/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
deleted file mode 100644
index 20e5113..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java
+++ /dev/null
@@ -1,105 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import cuchaz.enigma.utils.Utils;
15
16public class ConstructorEntry implements BehaviorEntry {
17
18 private ClassEntry classEntry;
19 private Signature signature;
20
21 public ConstructorEntry(ClassEntry classEntry) {
22 this(classEntry, null);
23 }
24
25 public ConstructorEntry(ClassEntry classEntry, Signature signature) {
26 if (classEntry == null) {
27 throw new IllegalArgumentException("Class cannot be null!");
28 }
29
30 this.classEntry = classEntry;
31 this.signature = signature;
32 }
33
34 public ConstructorEntry(ConstructorEntry other, String newClassName) {
35 this.classEntry = new ClassEntry(newClassName);
36 this.signature = other.signature;
37 }
38
39 @Override
40 public ClassEntry getClassEntry() {
41 return this.classEntry;
42 }
43
44 @Override
45 public String getName() {
46 if (isStatic()) {
47 return "<clinit>";
48 }
49 return "<init>";
50 }
51
52 public boolean isStatic() {
53 return this.signature == null;
54 }
55
56 @Override
57 public Signature getSignature() {
58 return this.signature;
59 }
60
61 @Override
62 public String getClassName() {
63 return this.classEntry.getName();
64 }
65
66 @Override
67 public ConstructorEntry cloneToNewClass(ClassEntry classEntry) {
68 return new ConstructorEntry(this, classEntry.getName());
69 }
70
71 @Override
72 public int hashCode() {
73 if (isStatic()) {
74 return Utils.combineHashesOrdered(this.classEntry);
75 } else {
76 return Utils.combineHashesOrdered(this.classEntry, this.signature);
77 }
78 }
79
80 @Override
81 public boolean equals(Object other) {
82 return other instanceof ConstructorEntry && equals((ConstructorEntry) other);
83 }
84
85 public boolean equals(ConstructorEntry other) {
86 if (isStatic() != other.isStatic()) {
87 return false;
88 }
89
90 if (isStatic()) {
91 return this.classEntry.equals(other.classEntry);
92 } else {
93 return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature);
94 }
95 }
96
97 @Override
98 public String toString() {
99 if (isStatic()) {
100 return this.classEntry.getName() + "." + getName();
101 } else {
102 return this.classEntry.getName() + "." + getName() + this.signature;
103 }
104 }
105}
diff --git a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
new file mode 100644
index 0000000..1283267
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
@@ -0,0 +1,319 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.TranslationIndex;
17import cuchaz.enigma.bytecode.AccessFlags;
18
19import java.util.ArrayList;
20import java.util.List;
21import java.util.Map;
22
23public class DirectionalTranslator implements Translator {
24
25 private final TranslationDirection direction;
26 private final Map<String, ClassMapping> classes;
27 private final TranslationIndex index;
28
29 public DirectionalTranslator(ReferencedEntryPool entryPool) {
30 this.direction = null;
31 this.classes = Maps.newHashMap();
32 this.index = new TranslationIndex(entryPool);
33 }
34
35 public DirectionalTranslator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) {
36 this.direction = direction;
37 this.classes = classes;
38 this.index = index;
39 }
40
41 public TranslationDirection getDirection() {
42 return direction;
43 }
44
45 public TranslationIndex getTranslationIndex() {
46 return index;
47 }
48
49 @Override
50 public ClassEntry getTranslatedClass(ClassEntry entry) {
51 String className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
52 return new ClassEntry(className);
53 }
54
55 @Override
56 public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) {
57 String className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
58 return new ClassDefEntry(className, getClassModifier(entry).transform(entry.getAccess()));
59 }
60
61 private String translateClassName(ClassEntry entry) {
62 // normal classes are easy
63 ClassMapping classMapping = this.classes.get(entry.getName());
64 if (classMapping == null) {
65 return entry.getName();
66 }
67 return classMapping.getTranslatedName(direction);
68 }
69
70 private String translateInnerClassName(ClassEntry entry) {
71 // translate as much of the class chain as we can
72 List<ClassMapping> mappingsChain = getClassMappingChain(entry);
73 String[] obfClassNames = entry.getName().split("\\$");
74 StringBuilder buf = new StringBuilder();
75 for (int i = 0; i < obfClassNames.length; i++) {
76 boolean isFirstClass = buf.length() == 0;
77 String className = null;
78 ClassMapping classMapping = mappingsChain.get(i);
79 if (classMapping != null) {
80 className = this.direction.choose(
81 classMapping.getDeobfName(),
82 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
83 );
84 }
85 if (className == null) {
86 className = obfClassNames[i];
87 }
88 if (!isFirstClass) {
89 buf.append("$");
90 }
91 buf.append(className);
92 }
93 return buf.toString();
94 }
95
96 @Override
97 public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) {
98 String translatedName = translateFieldName(entry);
99 if (translatedName == null) {
100 return entry;
101 }
102 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
103 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
104 AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess());
105 return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedAccess);
106 }
107
108 @Override
109 public FieldEntry getTranslatedField(FieldEntry entry) {
110 String translatedName = translateFieldName(entry);
111 if (translatedName == null) {
112 return null;
113 }
114 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
115 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
116 return new FieldEntry(translatedOwner, translatedName, translatedDesc);
117 }
118
119 private String translateFieldName(FieldEntry entry) {
120 // resolve the class entry
121 ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry);
122 if (resolvedClassEntry != null) {
123 // look for the class
124 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
125 if (classMapping != null) {
126 // look for the field
127 FieldMapping mapping = classMapping.getFieldByObf(entry.getName(), entry.getDesc());
128 if (mapping != null) {
129 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
130 }
131 }
132 }
133 return null;
134 }
135
136 @Override
137 public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) {
138 String translatedName = translateMethodName(entry);
139 if (translatedName == null) {
140 return entry;
141 }
142 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
143 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
144 AccessFlags access = getMethodModifier(entry).transform(entry.getAccess());
145 return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, access);
146 }
147
148 @Override
149 public MethodEntry getTranslatedMethod(MethodEntry entry) {
150 String translatedName = translateMethodName(entry);
151 if (translatedName == null) {
152 return null;
153 }
154 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
155 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
156 return new MethodEntry(translatedOwner, translatedName, translatedDesc);
157 }
158
159 private String translateMethodName(MethodEntry entry) {
160 // resolve the class entry
161 ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry, true);
162 if (resolvedOwner != null) {
163 // look for class
164 ClassMapping classMapping = findClassMapping(resolvedOwner);
165 if (classMapping != null) {
166 // look for the method
167 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
168 if (mapping != null) {
169 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
170 }
171 }
172 }
173 return null;
174 }
175
176 @Override
177 public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) {
178 String translatedArgumentName = translateLocalVariableName(entry);
179 if (translatedArgumentName == null) {
180 translatedArgumentName = inheritLocalVariableName(entry);
181 }
182 if (translatedArgumentName == null) {
183 return null;
184 }
185 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
186 MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry());
187 return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName);
188 }
189
190 @Override
191 public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) {
192 String translatedArgumentName = translateLocalVariableName(entry);
193 if (translatedArgumentName == null) {
194 translatedArgumentName = inheritLocalVariableName(entry);
195 }
196 if (translatedArgumentName == null) {
197 return entry;
198 }
199 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
200 MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry());
201 TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc());
202 return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName, translatedTypeDesc);
203 }
204
205 // TODO: support not identical behavior (specific to constructor)
206 private String translateLocalVariableName(LocalVariableEntry entry) {
207 // look for identical behavior in superclasses
208 ClassEntry ownerEntry = entry.getOwnerClassEntry();
209 if (ownerEntry != null) {
210 // look for the class
211 ClassMapping classMapping = findClassMapping(ownerEntry);
212 if (classMapping != null) {
213 // look for the method
214 MethodMapping methodMapping = classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc());
215 if (methodMapping != null) {
216 int index = entry.getIndex();
217 return this.direction.choose(
218 methodMapping.getDeobfLocalVariableName(index),
219 methodMapping.getObfLocalVariableName(index)
220 );
221 }
222 }
223 }
224 return null;
225 }
226
227 private String inheritLocalVariableName(LocalVariableEntry entry) {
228 List<ClassEntry> ancestry = this.index.getAncestry(entry.getOwnerClassEntry());
229 // Check in mother class for the arg
230 for (ClassEntry ancestorEntry : ancestry) {
231 LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry);
232 if (this.index.entryExists(motherArg)) {
233 String result = translateLocalVariableName(motherArg);
234 if (result != null) {
235 return result;
236 }
237 }
238 }
239 return null;
240 }
241
242 @Override
243 public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) {
244 return desc.remap(name -> getTranslatedClass(new ClassEntry(name)).getName());
245 }
246
247 @Override
248 public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) {
249 List<TypeDescriptor> arguments = descriptor.getArgumentDescs();
250 List<TypeDescriptor> translatedArguments = new ArrayList<>(arguments.size());
251 for (TypeDescriptor argument : arguments) {
252 translatedArguments.add(getTranslatedTypeDesc(argument));
253 }
254 return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc()));
255 }
256
257 private ClassMapping findClassMapping(ClassEntry entry) {
258 List<ClassMapping> mappingChain = getClassMappingChain(entry);
259 return mappingChain.get(mappingChain.size() - 1);
260 }
261
262 private List<ClassMapping> getClassMappingChain(ClassEntry entry) {
263
264 // get a list of all the classes in the hierarchy
265 String[] parts = entry.getName().split("\\$");
266 List<ClassMapping> mappingsChain = Lists.newArrayList();
267
268 // get mappings for the outer class
269 ClassMapping outerClassMapping = this.classes.get(parts[0]);
270 mappingsChain.add(outerClassMapping);
271
272 for (int i = 1; i < parts.length; i++) {
273
274 // get mappings for the inner class
275 ClassMapping innerClassMapping = null;
276 if (outerClassMapping != null) {
277 innerClassMapping = this.direction.choose(
278 outerClassMapping.getInnerClassByObfSimple(parts[i]),
279 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
280 );
281 }
282 mappingsChain.add(innerClassMapping);
283 outerClassMapping = innerClassMapping;
284 }
285
286 assert (mappingsChain.size() == parts.length);
287 return mappingsChain;
288 }
289
290 private Mappings.EntryModifier getClassModifier(ClassEntry entry) {
291 ClassMapping classMapping = findClassMapping(entry);
292 if (classMapping != null) {
293 return classMapping.getModifier();
294 }
295 return Mappings.EntryModifier.UNCHANGED;
296 }
297
298 private Mappings.EntryModifier getFieldModifier(FieldEntry entry) {
299 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
300 if (classMapping != null) {
301 FieldMapping fieldMapping = classMapping.getFieldByObf(entry);
302 if (fieldMapping != null) {
303 return fieldMapping.getModifier();
304 }
305 }
306 return Mappings.EntryModifier.UNCHANGED;
307 }
308
309 private Mappings.EntryModifier getMethodModifier(MethodEntry entry) {
310 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
311 if (classMapping != null) {
312 MethodMapping methodMapping = classMapping.getMethodByObf(entry);
313 if (methodMapping != null) {
314 return methodMapping.getModifier();
315 }
316 }
317 return Mappings.EntryModifier.UNCHANGED;
318 }
319}
diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/Entry.java
index c79510b..eb783e9 100644
--- a/src/main/java/cuchaz/enigma/mapping/Entry.java
+++ b/src/main/java/cuchaz/enigma/mapping/Entry.java
@@ -16,7 +16,7 @@ public interface Entry {
16 16
17 String getClassName(); 17 String getClassName();
18 18
19 ClassEntry getClassEntry(); 19 ClassEntry getOwnerClassEntry();
20 20
21 Entry cloneToNewClass(ClassEntry classEntry); 21 Entry updateOwnership(ClassEntry classEntry);
22} 22}
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
index 993bb64..c20f6f5 100644
--- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
@@ -12,19 +12,8 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.analysis.JarIndex; 14import cuchaz.enigma.analysis.JarIndex;
15import javassist.*;
16import javassist.bytecode.Descriptor;
17import javassist.expr.ConstructorCall;
18import javassist.expr.FieldAccess;
19import javassist.expr.MethodCall;
20import javassist.expr.NewExpr;
21 15
22public class EntryFactory { 16public class EntryFactory {
23
24 public static ClassEntry getClassEntry(CtClass c) {
25 return new ClassEntry(Descriptor.toJvmName(c.getName()));
26 }
27
28 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { 17 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) {
29 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); 18 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
30 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); 19 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
@@ -38,95 +27,19 @@ public class EntryFactory {
38 return new ClassEntry(classMapping.getDeobfName()); 27 return new ClassEntry(classMapping.getDeobfName());
39 } 28 }
40 29
41 public static ClassEntry getSuperclassEntry(CtClass c) {
42 return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass()));
43 }
44
45 public static FieldEntry getFieldEntry(CtField field) {
46 return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor()));
47 }
48
49 public static FieldEntry getFieldEntry(FieldAccess call) {
50 return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature()));
51 }
52
53 public static FieldEntry getFieldEntry(String className, String name, String type) {
54 return new FieldEntry(new ClassEntry(className), name, new Type(type));
55 }
56
57 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { 30 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
58 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); 31 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc());
59 }
60
61 public static MethodEntry getMethodEntry(CtMethod method) {
62 return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor()));
63 }
64
65 public static MethodEntry getMethodEntry(MethodCall call) {
66 return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature()));
67 }
68
69 public static ConstructorEntry getConstructorEntry(CtConstructor constructor) {
70 if (constructor.isClassInitializer()) {
71 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()));
72 } else {
73 return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor()));
74 }
75 }
76
77 public static ConstructorEntry getConstructorEntry(ConstructorCall call) {
78 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature()));
79 }
80
81 public static ConstructorEntry getConstructorEntry(NewExpr call) {
82 return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature()));
83 }
84
85 public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) {
86 if (behavior instanceof CtMethod) {
87 return getMethodEntry((CtMethod) behavior);
88 } else if (behavior instanceof CtConstructor) {
89 return getConstructorEntry((CtConstructor) behavior);
90 }
91 throw new Error("behavior is neither Method nor Constructor!");
92 }
93
94 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) {
95 return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature));
96 }
97
98 public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) {
99 return getBehaviorEntry(new ClassEntry(className), behaviorName);
100 }
101
102 public static BehaviorEntry getBehaviorEntry(String className) {
103 return new ConstructorEntry(new ClassEntry(className));
104 }
105
106 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) {
107 switch (behaviorName) {
108 case "<init>":
109 return new ConstructorEntry(classEntry, behaviorSignature);
110 case "<clinit>":
111 return new ConstructorEntry(classEntry);
112 default:
113 return new MethodEntry(classEntry, behaviorName, behaviorSignature);
114 }
115 } 32 }
116 33
117 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { 34 public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) {
118 if (behaviorName.equals("<clinit>")) { 35 return new MethodEntry(classEntry, name, desc);
119 return new ConstructorEntry(classEntry);
120 } else {
121 throw new IllegalArgumentException("Only class initializers don't have signatures");
122 }
123 } 36 }
124 37
125 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { 38 public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) {
126 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); 39 return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc());
127 } 40 }
128 41
129 public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { 42 public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) {
130 return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); 43 return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping);
131 } 44 }
132} 45}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java
new file mode 100644
index 0000000..262c16c
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java
@@ -0,0 +1,34 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16
17public class FieldDefEntry extends FieldEntry {
18 private final AccessFlags access;
19
20 public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, AccessFlags access) {
21 super(ownerEntry, name, desc);
22 Preconditions.checkNotNull(access, "Field access cannot be null");
23 this.access = access;
24 }
25
26 public AccessFlags getAccess() {
27 return access;
28 }
29
30 @Override
31 public FieldDefEntry updateOwnership(ClassEntry owner) {
32 return new FieldDefEntry(owner, this.name, this.desc, access);
33 }
34}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
index 0f1f506..c118ac0 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
@@ -11,40 +11,29 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import cuchaz.enigma.utils.Utils; 15import cuchaz.enigma.utils.Utils;
15 16
16public class FieldEntry implements Entry { 17public class FieldEntry implements Entry {
17 18
18 private ClassEntry classEntry; 19 protected final ClassEntry ownerEntry;
19 private String name; 20 protected final String name;
20 private Type type; 21 protected final TypeDescriptor desc;
21 22
22 // NOTE: this argument order is important for the MethodReader/MethodWriter 23 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public FieldEntry(ClassEntry classEntry, String name, Type type) { 24 public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
24 if (classEntry == null) { 25 Preconditions.checkNotNull(ownerEntry, "Owner cannot be null");
25 throw new IllegalArgumentException("Class cannot be null!"); 26 Preconditions.checkNotNull(name, "Field name cannot be null");
26 } 27 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
27 if (name == null) {
28 throw new IllegalArgumentException("Field name cannot be null!");
29 }
30 if (type == null) {
31 throw new IllegalArgumentException("Field type cannot be null!");
32 }
33 28
34 this.classEntry = classEntry; 29 this.ownerEntry = ownerEntry;
35 this.name = name; 30 this.name = name;
36 this.type = type; 31 this.desc = desc;
37 }
38
39 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) {
40 this.classEntry = newClassEntry;
41 this.name = other.name;
42 this.type = other.type;
43 } 32 }
44 33
45 @Override 34 @Override
46 public ClassEntry getClassEntry() { 35 public ClassEntry getOwnerClassEntry() {
47 return this.classEntry; 36 return this.ownerEntry;
48 } 37 }
49 38
50 @Override 39 @Override
@@ -54,21 +43,21 @@ public class FieldEntry implements Entry {
54 43
55 @Override 44 @Override
56 public String getClassName() { 45 public String getClassName() {
57 return this.classEntry.getName(); 46 return this.ownerEntry.getName();
58 } 47 }
59 48
60 public Type getType() { 49 public TypeDescriptor getDesc() {
61 return this.type; 50 return this.desc;
62 } 51 }
63 52
64 @Override 53 @Override
65 public FieldEntry cloneToNewClass(ClassEntry classEntry) { 54 public FieldEntry updateOwnership(ClassEntry owner) {
66 return new FieldEntry(this, classEntry); 55 return new FieldEntry(owner, this.name, this.desc);
67 } 56 }
68 57
69 @Override 58 @Override
70 public int hashCode() { 59 public int hashCode() {
71 return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); 60 return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc);
72 } 61 }
73 62
74 @Override 63 @Override
@@ -77,11 +66,11 @@ public class FieldEntry implements Entry {
77 } 66 }
78 67
79 public boolean equals(FieldEntry other) { 68 public boolean equals(FieldEntry other) {
80 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); 69 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc);
81 } 70 }
82 71
83 @Override 72 @Override
84 public String toString() { 73 public String toString() {
85 return this.classEntry.getName() + "." + this.name + ":" + this.type; 74 return this.ownerEntry.getName() + "." + this.name + ":" + this.desc;
86 } 75 }
87} 76}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
index cd761b4..3c46a37 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
@@ -17,26 +17,19 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
17 17
18 private String obfName; 18 private String obfName;
19 private String deobfName; 19 private String deobfName;
20 private Type obfType; 20 private TypeDescriptor obfDesc;
21 private Mappings.EntryModifier modifier; 21 private Mappings.EntryModifier modifier;
22 22
23 public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { 23 public FieldMapping(String obfName, TypeDescriptor obfDesc, String deobfName, Mappings.EntryModifier modifier) {
24 this.obfName = obfName; 24 this.obfName = obfName;
25 this.deobfName = NameValidator.validateFieldName(deobfName); 25 this.deobfName = NameValidator.validateFieldName(deobfName);
26 this.obfType = obfType; 26 this.obfDesc = obfDesc;
27 this.modifier = modifier; 27 this.modifier = modifier;
28 } 28 }
29 29
30 public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) {
31 this.obfName = other.obfName;
32 this.deobfName = other.deobfName;
33 this.modifier = other.modifier;
34 this.obfType = new Type(other.obfType, obfClassNameReplacer);
35 }
36
37 @Override 30 @Override
38 public FieldEntry getObfEntry(ClassEntry classEntry) { 31 public FieldEntry getObfEntry(ClassEntry classEntry) {
39 return new FieldEntry(classEntry, this.obfName, this.obfType); 32 return new FieldEntry(classEntry, this.obfName, this.obfDesc);
40 } 33 }
41 34
42 @Override 35 @Override
@@ -65,12 +58,12 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
65 this.deobfName = NameValidator.validateFieldName(val); 58 this.deobfName = NameValidator.validateFieldName(val);
66 } 59 }
67 60
68 public Type getObfType() { 61 public TypeDescriptor getObfDesc() {
69 return this.obfType; 62 return this.obfDesc;
70 } 63 }
71 64
72 public void setObfType(Type val) { 65 public void setObfDesc(TypeDescriptor val) {
73 this.obfType = val; 66 this.obfDesc = val;
74 } 67 }
75 68
76 public Mappings.EntryModifier getModifier() { 69 public Mappings.EntryModifier getModifier() {
@@ -83,21 +76,20 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
83 76
84 @Override 77 @Override
85 public int compareTo(FieldMapping other) { 78 public int compareTo(FieldMapping other) {
86 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); 79 return (this.obfName + this.obfDesc).compareTo(other.obfName + other.obfDesc);
87 } 80 }
88 81
89 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 82 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
90 // rename obf classes in the type 83 // rename obf classes in the desc
91 Type newType = new Type(this.obfType, className -> 84 TypeDescriptor newDesc = this.obfDesc.remap(className -> {
92 {
93 if (className.equals(oldObfClassName)) { 85 if (className.equals(oldObfClassName)) {
94 return newObfClassName; 86 return newObfClassName;
95 } 87 }
96 return null; 88 return className;
97 }); 89 });
98 90
99 if (!newType.equals(this.obfType)) { 91 if (!newDesc.equals(this.obfDesc)) {
100 this.obfType = newType; 92 this.obfDesc = newDesc;
101 return true; 93 return true;
102 } 94 }
103 return false; 95 return false;
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java
new file mode 100644
index 0000000..cc677c5
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java
@@ -0,0 +1,75 @@
1package cuchaz.enigma.mapping;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.utils.Utils;
5
6/**
7 * TypeDescriptor...
8 * Created by Thog
9 * 19/10/2016
10 */
11public class LocalVariableDefEntry extends LocalVariableEntry {
12
13 protected final MethodDefEntry ownerEntry;
14 protected final TypeDescriptor desc;
15
16 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) {
17 super(ownerEntry, index, name);
18 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
19
20 this.ownerEntry = ownerEntry;
21 this.desc = desc;
22 }
23
24 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name) {
25 super(ownerEntry, index, name);
26
27 this.ownerEntry = ownerEntry;
28
29 int namedIndex = getNamedIndex();
30 if (namedIndex < 0) {
31 this.desc = TypeDescriptor.of(ownerEntry.getOwnerClassEntry().getName());
32 } else {
33 this.desc = ownerEntry.getDesc().getArgumentDescs().get(namedIndex);
34 }
35 }
36
37 @Override
38 public MethodDefEntry getOwnerEntry() {
39 return this.ownerEntry;
40 }
41
42 public TypeDescriptor getDesc() {
43 return desc;
44 }
45
46 public int getNamedIndex() {
47 // If we're not static, "this" is bound to index 0
48 int indexOffset = ownerEntry.getAccess().isStatic() ? 0 : 1;
49 return index - indexOffset;
50 }
51
52 @Override
53 public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) {
54 return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, desc);
55 }
56
57 @Override
58 public int hashCode() {
59 return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
60 }
61
62 @Override
63 public boolean equals(Object other) {
64 return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other);
65 }
66
67 public boolean equals(LocalVariableDefEntry other) {
68 return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index;
69 }
70
71 @Override
72 public String toString() {
73 return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
74 }
75}
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
index 2bb5e3f..dcfd0ff 100644
--- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
@@ -1,52 +1,31 @@
1package cuchaz.enigma.mapping; 1package cuchaz.enigma.mapping;
2 2
3import com.google.common.base.Preconditions;
3import cuchaz.enigma.utils.Utils; 4import cuchaz.enigma.utils.Utils;
4 5
5/** 6/**
6 * Desc... 7 * TypeDescriptor...
7 * Created by Thog 8 * Created by Thog
8 * 19/10/2016 9 * 19/10/2016
9 */ 10 */
10public class LocalVariableEntry implements Entry { 11public class LocalVariableEntry implements Entry {
11 12
12 protected final BehaviorEntry behaviorEntry; 13 protected final MethodEntry ownerEntry;
13 protected final String name; 14 protected final String name;
14 protected final Type type;
15 protected final int index; 15 protected final int index;
16 16
17 public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { 17 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) {
18 if (behaviorEntry == null) { 18 Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null");
19 throw new IllegalArgumentException("Behavior cannot be null!"); 19 Preconditions.checkNotNull(name, "Variable name cannot be null");
20 } 20 Preconditions.checkArgument(index >= 0, "Index must be positive");
21 if (index < 0) { 21
22 throw new IllegalArgumentException("Index must be non-negative!"); 22 this.ownerEntry = ownerEntry;
23 }
24 if (name == null) {
25 throw new IllegalArgumentException("Variable name cannot be null!");
26 }
27 if (type == null) {
28 throw new IllegalArgumentException("Variable type cannot be null!");
29 }
30
31 this.behaviorEntry = behaviorEntry;
32 this.name = name; 23 this.name = name;
33 this.type = type;
34 this.index = index; 24 this.index = index;
35 } 25 }
36 26
37 public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { 27 public MethodEntry getOwnerEntry() {
38 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); 28 return this.ownerEntry;
39 this.name = other.name;
40 this.type = other.type;
41 this.index = other.index;
42 }
43
44 public BehaviorEntry getBehaviorEntry() {
45 return this.behaviorEntry;
46 }
47
48 public Type getType() {
49 return type;
50 } 29 }
51 30
52 public int getIndex() { 31 public int getIndex() {
@@ -59,31 +38,31 @@ public class LocalVariableEntry implements Entry {
59 } 38 }
60 39
61 @Override 40 @Override
62 public ClassEntry getClassEntry() { 41 public ClassEntry getOwnerClassEntry() {
63 return this.behaviorEntry.getClassEntry(); 42 return this.ownerEntry.getOwnerClassEntry();
64 } 43 }
65 44
66 @Override 45 @Override
67 public String getClassName() { 46 public String getClassName() {
68 return this.behaviorEntry.getClassName(); 47 return this.ownerEntry.getClassName();
69 } 48 }
70 49
71 @Override 50 @Override
72 public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { 51 public LocalVariableEntry updateOwnership(ClassEntry classEntry) {
73 return new LocalVariableEntry(this, classEntry); 52 return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name);
74 } 53 }
75 54
76 public String getMethodName() { 55 public String getMethodName() {
77 return this.behaviorEntry.getName(); 56 return this.ownerEntry.getName();
78 } 57 }
79 58
80 public Signature getMethodSignature() { 59 public MethodDescriptor getMethodDesc() {
81 return this.behaviorEntry.getSignature(); 60 return this.ownerEntry.getDesc();
82 } 61 }
83 62
84 @Override 63 @Override
85 public int hashCode() { 64 public int hashCode() {
86 return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); 65 return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index));
87 } 66 }
88 67
89 @Override 68 @Override
@@ -92,11 +71,11 @@ public class LocalVariableEntry implements Entry {
92 } 71 }
93 72
94 public boolean equals(LocalVariableEntry other) { 73 public boolean equals(LocalVariableEntry other) {
95 return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; 74 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index;
96 } 75 }
97 76
98 @Override 77 @Override
99 public String toString() { 78 public String toString() {
100 return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")"; 79 return this.ownerEntry + "(" + this.index + ":" + this.name + ")";
101 } 80 }
102} 81}
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
index 91ecd10..193c566 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
@@ -11,18 +11,18 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14public class ArgumentMapping implements Comparable<ArgumentMapping> { 14public class LocalVariableMapping implements Comparable<LocalVariableMapping> {
15 15
16 private int index; 16 private int index;
17 private String name; 17 private String name;
18 18
19 // NOTE: this argument order is important for the MethodReader/MethodWriter 19 // NOTE: this argument order is important for the MethodReader/MethodWriter
20 public ArgumentMapping(int index, String name) { 20 public LocalVariableMapping(int index, String name) {
21 this.index = index; 21 this.index = index;
22 this.name = NameValidator.validateArgumentName(name); 22 this.name = NameValidator.validateArgumentName(name);
23 } 23 }
24 24
25 public ArgumentMapping(ArgumentMapping other) { 25 public LocalVariableMapping(LocalVariableMapping other) {
26 this.index = other.index; 26 this.index = other.index;
27 this.name = other.name; 27 this.name = other.name;
28 } 28 }
@@ -39,12 +39,12 @@ public class ArgumentMapping implements Comparable<ArgumentMapping> {
39 this.name = NameValidator.validateArgumentName(val); 39 this.name = NameValidator.validateArgumentName(val);
40 } 40 }
41 41
42 public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { 42 public LocalVariableEntry getObfEntry(MethodEntry methodEntry) {
43 return new ArgumentEntry(behaviorEntry, index, name); 43 return new LocalVariableEntry(methodEntry, index, name);
44 } 44 }
45 45
46 @Override 46 @Override
47 public int compareTo(ArgumentMapping other) { 47 public int compareTo(LocalVariableMapping other) {
48 return Integer.compare(this.index, other.index); 48 return Integer.compare(this.index, other.index);
49 } 49 }
50} 50}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index cf78ca3..cc1ec9c 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -15,6 +15,7 @@ import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import com.google.common.collect.Sets; 16import com.google.common.collect.Sets;
17import cuchaz.enigma.analysis.TranslationIndex; 17import cuchaz.enigma.analysis.TranslationIndex;
18import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.throwables.MappingConflict; 19import cuchaz.enigma.throwables.MappingConflict;
19 20
20import java.io.File; 21import java.io.File;
@@ -96,11 +97,11 @@ public class Mappings {
96 97
97 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { 98 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
98 switch (direction) { 99 switch (direction) {
99 case Deobfuscating: 100 case DEOBFUSCATING:
100 101
101 return new Translator(direction, this.classesByObf, index); 102 return new DirectionalTranslator(direction, this.classesByObf, index);
102 103
103 case Obfuscating: 104 case OBFUSCATING:
104 105
105 // fill in the missing deobf class entries with obf entries 106 // fill in the missing deobf class entries with obf entries
106 Map<String, ClassMapping> classes = Maps.newHashMap(); 107 Map<String, ClassMapping> classes = Maps.newHashMap();
@@ -114,9 +115,9 @@ public class Mappings {
114 115
115 // translate the translation index 116 // translate the translation index
116 // NOTE: this isn't actually recursive 117 // NOTE: this isn't actually recursive
117 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); 118 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index));
118 119
119 return new Translator(direction, classes, deobfIndex); 120 return new DirectionalTranslator(direction, classes, deobfIndex);
120 121
121 default: 122 default:
122 throw new Error("Invalid translation direction!"); 123 throw new Error("Invalid translation direction!");
@@ -151,9 +152,9 @@ public class Mappings {
151 152
152 // add classes from method signatures 153 // add classes from method signatures
153 for (MethodMapping methodMapping : classMapping.methods()) { 154 for (MethodMapping methodMapping : classMapping.methods()) {
154 for (Type type : methodMapping.getObfSignature().types()) { 155 for (TypeDescriptor desc : methodMapping.getObfDesc().types()) {
155 if (type.hasClass()) { 156 if (desc.containsType()) {
156 classNames.add(type.getClassEntry().getClassName()); 157 classNames.add(desc.getOwnerEntry().getClassName());
157 } 158 }
158 } 159 }
159 } 160 }
@@ -165,9 +166,9 @@ public class Mappings {
165 return this.classesByDeobf.containsKey(deobfName); 166 return this.classesByDeobf.containsKey(deobfName);
166 } 167 }
167 168
168 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { 169 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) {
169 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 170 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
170 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 171 return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc);
171 } 172 }
172 173
173 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { 174 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
@@ -180,14 +181,14 @@ public class Mappings {
180 return false; 181 return false;
181 } 182 }
182 183
183 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { 184 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) {
184 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 185 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
185 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); 186 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor);
186 } 187 }
187 188
188 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 189 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
189 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); 190 ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName());
190 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); 191 return classMapping != null && classMapping.containsArgument(obfMethodEntry, name);
191 } 192 }
192 193
193 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { 194 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
@@ -239,5 +240,19 @@ public class Mappings {
239 public String getFormattedName() { 240 public String getFormattedName() {
240 return " ACC:" + super.toString(); 241 return " ACC:" + super.toString();
241 } 242 }
243
244 public AccessFlags transform(AccessFlags access) {
245 switch (this) {
246 case PUBLIC:
247 return access.setPublic();
248 case PROTECTED:
249 return access.setProtected();
250 case PRIVATE:
251 return access.setPrivate();
252 case UNCHANGED:
253 default:
254 return access;
255 }
256 }
242 } 257 }
243} 258}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
index 172641b..4d5be2f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
@@ -23,7 +23,7 @@ public class MappingsChecker {
23 private Map<ClassEntry, ClassMapping> droppedClassMappings; 23 private Map<ClassEntry, ClassMapping> droppedClassMappings;
24 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; 24 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
25 private Map<FieldEntry, FieldMapping> droppedFieldMappings; 25 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
26 private Map<BehaviorEntry, MethodMapping> droppedMethodMappings; 26 private Map<MethodEntry, MethodMapping> droppedMethodMappings;
27 27
28 public MappingsChecker(JarIndex index) { 28 public MappingsChecker(JarIndex index) {
29 this.index = index; 29 this.index = index;
@@ -45,7 +45,7 @@ public class MappingsChecker {
45 return this.droppedFieldMappings; 45 return this.droppedFieldMappings;
46 } 46 }
47 47
48 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { 48 public Map<MethodEntry, MethodMapping> getDroppedMethodMappings() {
49 return this.droppedMethodMappings; 49 return this.droppedMethodMappings;
50 } 50 }
51 51
@@ -77,10 +77,10 @@ public class MappingsChecker {
77 77
78 // check methods 78 // check methods
79 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 79 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
80 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); 80 MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping);
81 if (!this.index.containsObfBehavior(obfBehaviorEntry)) { 81 if (!this.index.containsObfMethod(obfMethodEntry)) {
82 classMapping.removeMethodMapping(methodMapping); 82 classMapping.removeMethodMapping(methodMapping);
83 this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); 83 this.droppedMethodMappings.put(obfMethodEntry, methodMapping);
84 } 84 }
85 } 85 }
86 86
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
index a0d4313..d1d5634 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
@@ -123,8 +123,8 @@ public class MappingsEnigmaReader {
123 return mappings; 123 return mappings;
124 } 124 }
125 125
126 private ArgumentMapping readArgument(String[] parts) { 126 private LocalVariableMapping readArgument(String[] parts) {
127 return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); 127 return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]);
128 } 128 }
129 129
130 private ClassMapping readClass(String[] parts, boolean makeSimple) { 130 private ClassMapping readClass(String[] parts, boolean makeSimple) {
@@ -150,27 +150,27 @@ public class MappingsEnigmaReader {
150 if (parts.length == 4) { 150 if (parts.length == 4) {
151 boolean access = parts[3].startsWith("ACC:"); 151 boolean access = parts[3].startsWith("ACC:");
152 if (access) 152 if (access)
153 mapping = new FieldMapping(parts[1], new Type(parts[2]), null, 153 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null,
154 Mappings.EntryModifier.valueOf(parts[3].substring(4))); 154 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
155 else 155 else
156 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); 156 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
157 } else if (parts.length == 5) 157 } else if (parts.length == 5)
158 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); 158 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
159 return mapping; 159 return mapping;
160 } 160 }
161 161
162 private MethodMapping readMethod(String[] parts) { 162 private MethodMapping readMethod(String[] parts) {
163 MethodMapping mapping = null; 163 MethodMapping mapping = null;
164 if (parts.length == 3) 164 if (parts.length == 3)
165 mapping = new MethodMapping(parts[1], new Signature(parts[2])); 165 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]));
166 else if (parts.length == 4) { 166 else if (parts.length == 4) {
167 boolean access = parts[3].startsWith("ACC:"); 167 boolean access = parts[3].startsWith("ACC:");
168 if (access) 168 if (access)
169 mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); 169 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
170 else 170 else
171 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); 171 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]);
172 } else if (parts.length == 5) 172 } else if (parts.length == 5)
173 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], 173 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2],
174 Mappings.EntryModifier.valueOf(parts[4].substring(4))); 174 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
175 return mapping; 175 return mapping;
176 } 176 }
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
index ba1b258..1929977 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
@@ -127,29 +127,29 @@ public class MappingsEnigmaWriter {
127 127
128 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { 128 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) {
129 if (fieldMapping.getDeobfName() == null) 129 if (fieldMapping.getDeobfName() == null)
130 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), 130 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(),
131 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 131 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
132 else 132 else
133 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), 133 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfDesc().toString(),
134 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 134 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
135 } 135 }
136 136
137 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { 137 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
138 if (methodMapping.getDeobfName() == null) { 138 if (methodMapping.getDeobfName() == null) {
139 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), 139 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(),
140 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); 140 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
141 } else { 141 } else {
142 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), 142 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfDesc(),
143 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); 143 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
144 } 144 }
145 145
146 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { 146 for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) {
147 write(out, argumentMapping, depth + 1); 147 write(out, localVariableMapping, depth + 1);
148 } 148 }
149 } 149 }
150 150
151 private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { 151 private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) {
152 out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); 152 out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName());
153 } 153 }
154 154
155 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { 155 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
index 7126d2b..e215a0f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -25,12 +25,14 @@ import java.util.zip.GZIPOutputStream;
25 25
26public class MappingsRenamer { 26public class MappingsRenamer {
27 27
28 private JarIndex index; 28 private final JarIndex index;
29 private final ReferencedEntryPool entryPool;
29 private Mappings mappings; 30 private Mappings mappings;
30 31
31 public MappingsRenamer(JarIndex index, Mappings mappings) { 32 public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) {
32 this.index = index; 33 this.index = index;
33 this.mappings = mappings; 34 this.mappings = mappings;
35 this.entryPool = entryPool;
34 } 36 }
35 37
36 public void setMappings(Mappings mappings) { 38 public void setMappings(Mappings mappings) {
@@ -46,7 +48,7 @@ public class MappingsRenamer {
46 48
47 if (deobfName != null) { 49 if (deobfName != null) {
48 // make sure we don't rename to an existing obf or deobf class 50 // make sure we don't rename to an existing obf or deobf class
49 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { 51 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) {
50 throw new IllegalNameException(deobfName, "There is already a class with that name"); 52 throw new IllegalNameException(deobfName, "There is already a class with that name");
51 } 53 }
52 } 54 }
@@ -87,13 +89,13 @@ public class MappingsRenamer {
87 89
88 public void setFieldName(FieldEntry obf, String deobfName) { 90 public void setFieldName(FieldEntry obf, String deobfName) {
89 deobfName = NameValidator.validateFieldName(deobfName); 91 deobfName = NameValidator.validateFieldName(deobfName);
90 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); 92 FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
91 ClassEntry definedClass = null; 93 ClassEntry definedClass = null;
92 if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) 94 if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry))
93 definedClass = obf.getClassEntry(); 95 definedClass = obf.getOwnerClassEntry();
94 else { 96 else {
95 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { 97 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) {
96 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { 98 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) {
97 definedClass = ancestorEntry; 99 definedClass = ancestorEntry;
98 break; 100 break;
99 } 101 }
@@ -101,42 +103,44 @@ public class MappingsRenamer {
101 } 103 }
102 104
103 if (definedClass != null) { 105 if (definedClass != null) {
104 String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); 106 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
107 String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName();
105 if (className == null) 108 if (className == null)
106 className = definedClass.getClassName(); 109 className = definedClass.getClassName();
107 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); 110 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
108 } 111 }
109 112
110 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 113 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
111 classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); 114 classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName);
112 } 115 }
113 116
114 public void removeFieldMapping(FieldEntry obf) { 117 public void removeFieldMapping(FieldEntry obf) {
115 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 118 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
116 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); 119 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc()));
117 } 120 }
118 121
119 public void markFieldAsDeobfuscated(FieldEntry obf) { 122 public void markFieldAsDeobfuscated(FieldEntry obf) {
120 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 123 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
121 classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); 124 classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName());
122 } 125 }
123 126
124 private void validateMethodTreeName(MethodEntry entry, String deobfName) { 127 private void validateMethodTreeName(MethodEntry entry, String deobfName) {
125 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); 128 MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc());
126 129
127 // TODO: Verify if I don't break things 130 // TODO: Verify if I don't break things
128 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 131 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
129 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) 132 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc()))
130 || index.containsObfBehavior(targetEntry)) { 133 || index.containsObfMethod(targetEntry)) {
131 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); 134 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
135 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName();
132 if (deobfClassName == null) { 136 if (deobfClassName == null) {
133 deobfClassName = entry.getClassName(); 137 deobfClassName = entry.getClassName();
134 } 138 }
135 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 139 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
136 } 140 }
137 141
138 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { 142 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) {
139 validateMethodTreeName(entry.cloneToNewClass(child), deobfName); 143 validateMethodTreeName(entry.updateOwnership(child), deobfName);
140 } 144 }
141 } 145 }
142 146
@@ -155,20 +159,21 @@ public class MappingsRenamer {
155 159
156 public void setMethodName(MethodEntry obf, String deobfName) { 160 public void setMethodName(MethodEntry obf, String deobfName) {
157 deobfName = NameValidator.validateMethodName(deobfName); 161 deobfName = NameValidator.validateMethodName(deobfName);
158 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); 162 MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
159 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 163 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
160 164
161 // TODO: Verify if I don't break things 165 // TODO: Verify if I don't break things
162 if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) 166 if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc()))
163 || index.containsObfBehavior(targetEntry)) { 167 || index.containsObfMethod(targetEntry)) {
164 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); 168 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
169 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName();
165 if (deobfClassName == null) { 170 if (deobfClassName == null) {
166 deobfClassName = obf.getClassName(); 171 deobfClassName = obf.getClassName();
167 } 172 }
168 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 173 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
169 } 174 }
170 175
171 classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); 176 classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName);
172 } 177 }
173 178
174 public void removeMethodTreeMapping(MethodEntry obf) { 179 public void removeMethodTreeMapping(MethodEntry obf) {
@@ -176,8 +181,8 @@ public class MappingsRenamer {
176 } 181 }
177 182
178 public void removeMethodMapping(MethodEntry obf) { 183 public void removeMethodMapping(MethodEntry obf) {
179 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 184 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
180 classMapping.setMethodName(obf.getName(), obf.getSignature(), null); 185 classMapping.setMethodName(obf.getName(), obf.getDesc(), null);
181 } 186 }
182 187
183 public void markMethodTreeAsDeobfuscated(MethodEntry obf) { 188 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
@@ -185,30 +190,25 @@ public class MappingsRenamer {
185 } 190 }
186 191
187 public void markMethodAsDeobfuscated(MethodEntry obf) { 192 public void markMethodAsDeobfuscated(MethodEntry obf) {
188 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 193 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
189 classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); 194 classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName());
190 } 195 }
191 196
192 public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { 197 public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) {
193 if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { 198 MethodEntry obfMethod = obf.getOwnerEntry();
194 setArgumentName(obf, deobfName);
195 return;
196 }
197
198 MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry();
199 199
200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); 200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod);
201 for (MethodEntry entry : implementations) { 201 for (MethodEntry entry : implementations) {
202 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 202 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
203 if (classMapping != null) { 203 if (classMapping != null) {
204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); 204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
206 // TODO: Verify if I don't break things 206 // TODO: Verify if I don't break things
207 if (mapping != null) { 207 if (mapping != null) {
208 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 208 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
209 if (argumentMapping.getIndex() != obf.getIndex()) { 209 if (localVariableMapping.getIndex() != obf.getIndex()) {
210 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 210 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
211 || argumentMapping.getName().equals(deobfName)) { 211 || localVariableMapping.getName().equals(deobfName)) {
212 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 212 throw new IllegalNameException(deobfName, "There is already an argument with that name");
213 } 213 }
214 } 214 }
@@ -218,45 +218,45 @@ public class MappingsRenamer {
218 } 218 }
219 219
220 for (MethodEntry entry : implementations) { 220 for (MethodEntry entry : implementations) {
221 setArgumentName(new ArgumentEntry(obf, entry), deobfName); 221 setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName()), deobfName);
222 } 222 }
223 } 223 }
224 224
225 public void setArgumentName(ArgumentEntry obf, String deobfName) { 225 public void setLocalVariableName(LocalVariableEntry obf, String deobfName) {
226 deobfName = NameValidator.validateArgumentName(deobfName); 226 deobfName = NameValidator.validateArgumentName(deobfName);
227 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 227 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); 228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc());
229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
230 // TODO: Verify if I don't break things 230 // TODO: Verify if I don't break things
231 if (mapping != null) { 231 if (mapping != null) {
232 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 232 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
233 if (argumentMapping.getIndex() != obf.getIndex()) { 233 if (localVariableMapping.getIndex() != obf.getIndex()) {
234 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 234 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
235 || argumentMapping.getName().equals(deobfName)) { 235 || localVariableMapping.getName().equals(deobfName)) {
236 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 236 throw new IllegalNameException(deobfName, "There is already an argument with that name");
237 } 237 }
238 } 238 }
239 } 239 }
240 } 240 }
241 241
242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); 242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName);
243 } 243 }
244 244
245 public void removeArgumentMapping(ArgumentEntry obf) { 245 public void removeLocalVariableMapping(LocalVariableEntry obf) {
246 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 246 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); 247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex());
248 } 248 }
249 249
250 public void markArgumentAsDeobfuscated(ArgumentEntry obf) { 250 public void markArgumentAsDeobfuscated(LocalVariableEntry obf) {
251 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 251 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); 252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName());
253 } 253 }
254 254
255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { 255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
256 classMapping.removeFieldMapping(fieldMapping); 256 classMapping.removeFieldMapping(fieldMapping);
257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { 258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) {
259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { 259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) {
260 targetClassMapping.addFieldMapping(fieldMapping); 260 targetClassMapping.addFieldMapping(fieldMapping);
261 return true; 261 return true;
262 } else { 262 } else {
@@ -269,12 +269,12 @@ public class MappingsRenamer {
269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { 269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
270 classMapping.removeMethodMapping(methodMapping); 270 classMapping.removeMethodMapping(methodMapping);
271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { 272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) {
273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { 273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) {
274 targetClassMapping.addMethodMapping(methodMapping); 274 targetClassMapping.addMethodMapping(methodMapping);
275 return true; 275 return true;
276 } else { 276 } else {
277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); 277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc());
278 } 278 }
279 } 279 }
280 return false; 280 return false;
@@ -326,12 +326,35 @@ public class MappingsRenamer {
326 } 326 }
327 327
328 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { 328 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) {
329 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 329 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
330 classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); 330 classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier);
331 }
332
333 public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
335 classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier);
336 }
337
338 public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) {
339 ClassMapping classMapping = getOrCreateClassMapping(obfEntry);
340 return classMapping.getModifier();
331 } 341 }
332 342
333 public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) { 343 public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 344 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
335 classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); 345 FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry);
346 if (fieldMapping == null) {
347 return Mappings.EntryModifier.UNCHANGED;
348 }
349 return fieldMapping.getModifier();
350 }
351
352 public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) {
353 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
354 MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry);
355 if (methodMapping == null) {
356 return Mappings.EntryModifier.UNCHANGED;
357 }
358 return methodMapping.getModifier();
336 } 359 }
337} 360}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
index b0eb826..95daa73 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
@@ -19,7 +19,7 @@ public class MappingsSRGWriter {
19 } 19 }
20 file.createNewFile(); 20 file.createNewFile();
21 21
22 TranslationIndex index = new TranslationIndex(); 22 TranslationIndex index = new TranslationIndex(new ReferencedEntryPool());
23 23
24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); 24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8));
25 List<String> fieldMappings = new ArrayList<>(); 25 List<String> fieldMappings = new ArrayList<>();
@@ -43,7 +43,7 @@ public class MappingsSRGWriter {
43 } 43 }
44 44
45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { 45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) {
46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 46 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
47 } 47 }
48 } 48 }
49 49
@@ -52,7 +52,7 @@ public class MappingsSRGWriter {
52 } 52 }
53 53
54 for (MethodMapping methodMapping : sorted(classMapping.methods())) { 54 for (MethodMapping methodMapping : sorted(classMapping.methods())) {
55 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); 55 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
56 } 56 }
57 } 57 }
58 for (String fd : fieldMappings) { 58 for (String fd : fieldMappings) {
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
index dfe9e88..e635fa1 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
@@ -20,11 +20,11 @@ public class MappingsTinyReader {
20 } 20 }
21 21
22 public FieldMapping readField(String[] parts) { 22 public FieldMapping readField(String[] parts) {
23 return new FieldMapping(parts[3], new Type(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); 23 return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED);
24 } 24 }
25 25
26 public MethodMapping readMethod(String[] parts) { 26 public MethodMapping readMethod(String[] parts) {
27 return new MethodMapping(parts[3], new Signature(parts[2]), parts[4]); 27 return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]);
28 } 28 }
29 29
30 public Mappings read(File file) throws IOException, MappingParseException { 30 public Mappings read(File file) throws IOException, MappingParseException {
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java
new file mode 100644
index 0000000..d6a160d
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java
@@ -0,0 +1,35 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16
17public class MethodDefEntry extends MethodEntry {
18
19 private final AccessFlags access;
20
21 public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, AccessFlags access) {
22 super(classEntry, name, descriptor);
23 Preconditions.checkNotNull(access, "Method access cannot be null");
24 this.access = access;
25 }
26
27 public AccessFlags getAccess() {
28 return access;
29 }
30
31 @Override
32 public MethodDefEntry updateOwnership(ClassEntry classEntry) {
33 return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, access);
34 }
35}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
new file mode 100644
index 0000000..210ada0
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
@@ -0,0 +1,113 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.utils.Utils;
16
17import java.util.ArrayList;
18import java.util.List;
19import java.util.function.Function;
20
21public class MethodDescriptor {
22
23 private List<TypeDescriptor> argumentDescs;
24 private TypeDescriptor returnDesc;
25
26 public MethodDescriptor(String desc) {
27 try {
28 this.argumentDescs = Lists.newArrayList();
29 int i = 0;
30 while (i < desc.length()) {
31 char c = desc.charAt(i);
32 if (c == '(') {
33 assert (this.argumentDescs.isEmpty());
34 assert (this.returnDesc == null);
35 i++;
36 } else if (c == ')') {
37 i++;
38 break;
39 } else {
40 String type = TypeDescriptor.parseFirst(desc.substring(i));
41 this.argumentDescs.add(new TypeDescriptor(type));
42 i += type.length();
43 }
44 }
45 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i)));
46 } catch (Exception ex) {
47 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex);
48 }
49 }
50
51 public MethodDescriptor(List<TypeDescriptor> argumentDescs, TypeDescriptor returnDesc) {
52 this.argumentDescs = argumentDescs;
53 this.returnDesc = returnDesc;
54 }
55
56 public List<TypeDescriptor> getArgumentDescs() {
57 return this.argumentDescs;
58 }
59
60 public TypeDescriptor getReturnDesc() {
61 return this.returnDesc;
62 }
63
64 @Override
65 public String toString() {
66 StringBuilder buf = new StringBuilder();
67 buf.append("(");
68 for (TypeDescriptor desc : this.argumentDescs) {
69 buf.append(desc);
70 }
71 buf.append(")");
72 buf.append(this.returnDesc);
73 return buf.toString();
74 }
75
76 public Iterable<TypeDescriptor> types() {
77 List<TypeDescriptor> descs = Lists.newArrayList();
78 descs.addAll(this.argumentDescs);
79 descs.add(this.returnDesc);
80 return descs;
81 }
82
83 @Override
84 public boolean equals(Object other) {
85 return other instanceof MethodDescriptor && equals((MethodDescriptor) other);
86 }
87
88 public boolean equals(MethodDescriptor other) {
89 return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc);
90 }
91
92 @Override
93 public int hashCode() {
94 return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode());
95 }
96
97 public boolean hasClass(ClassEntry classEntry) {
98 for (TypeDescriptor desc : types()) {
99 if (desc.containsType() && desc.getOwnerEntry().equals(classEntry)) {
100 return true;
101 }
102 }
103 return false;
104 }
105
106 public MethodDescriptor remap(Function<String, String> remapper) {
107 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size());
108 for (TypeDescriptor desc : this.argumentDescs) {
109 argumentDescs.add(desc.remap(remapper));
110 }
111 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
112 }
113}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
index 9c3058c..f8a5ff1 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
@@ -11,41 +11,27 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import cuchaz.enigma.utils.Utils; 15import cuchaz.enigma.utils.Utils;
15 16
16public class MethodEntry implements BehaviorEntry { 17public class MethodEntry implements Entry {
17 18
18 private ClassEntry classEntry; 19 protected final ClassEntry classEntry;
19 private String name; 20 protected final String name;
20 private Signature signature; 21 protected final MethodDescriptor descriptor;
21 22
22 public MethodEntry(ClassEntry classEntry, String name, Signature signature) { 23 public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) {
23 if (classEntry == null) { 24 Preconditions.checkNotNull(classEntry, "Class cannot be null");
24 throw new IllegalArgumentException("Class cannot be null!"); 25 Preconditions.checkNotNull(name, "Method name cannot be null");
25 } 26 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
26 if (name == null) {
27 throw new IllegalArgumentException("Method name cannot be null!");
28 }
29 if (signature == null) {
30 throw new IllegalArgumentException("Method signature cannot be null!");
31 }
32 if (name.startsWith("<")) {
33 throw new IllegalArgumentException("Don't use MethodEntry for a constructor!");
34 }
35 27
36 this.classEntry = classEntry; 28 this.classEntry = classEntry;
37 this.name = name; 29 this.name = name;
38 this.signature = signature; 30 this.descriptor = descriptor;
39 }
40
41 public MethodEntry(MethodEntry other, String newClassName) {
42 this.classEntry = new ClassEntry(newClassName);
43 this.name = other.name;
44 this.signature = other.signature;
45 } 31 }
46 32
47 @Override 33 @Override
48 public ClassEntry getClassEntry() { 34 public ClassEntry getOwnerClassEntry() {
49 return this.classEntry; 35 return this.classEntry;
50 } 36 }
51 37
@@ -54,9 +40,12 @@ public class MethodEntry implements BehaviorEntry {
54 return this.name; 40 return this.name;
55 } 41 }
56 42
57 @Override 43 public MethodDescriptor getDesc() {
58 public Signature getSignature() { 44 return this.descriptor;
59 return this.signature; 45 }
46
47 public boolean isConstructor() {
48 return name.equals("<init>") || name.equals("<clinit>");
60 } 49 }
61 50
62 @Override 51 @Override
@@ -65,13 +54,13 @@ public class MethodEntry implements BehaviorEntry {
65 } 54 }
66 55
67 @Override 56 @Override
68 public MethodEntry cloneToNewClass(ClassEntry classEntry) { 57 public MethodEntry updateOwnership(ClassEntry classEntry) {
69 return new MethodEntry(this, classEntry.getName()); 58 return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor);
70 } 59 }
71 60
72 @Override 61 @Override
73 public int hashCode() { 62 public int hashCode() {
74 return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); 63 return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor);
75 } 64 }
76 65
77 @Override 66 @Override
@@ -80,11 +69,11 @@ public class MethodEntry implements BehaviorEntry {
80 } 69 }
81 70
82 public boolean equals(MethodEntry other) { 71 public boolean equals(MethodEntry other) {
83 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); 72 return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
84 } 73 }
85 74
86 @Override 75 @Override
87 public String toString() { 76 public String toString() {
88 return this.classEntry.getName() + "." + this.name + this.signature; 77 return this.classEntry.getName() + "." + this.name + this.descriptor;
89 } 78 }
90} 79}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
index 1524ce6..2f7fe53 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
@@ -11,50 +11,47 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.base.Preconditions;
14import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
15import cuchaz.enigma.throwables.IllegalNameException; 16import cuchaz.enigma.throwables.IllegalNameException;
16import cuchaz.enigma.throwables.MappingConflict; 17import cuchaz.enigma.throwables.MappingConflict;
17 18
18import java.util.Map; 19import java.util.Map;
19 20
20public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { 21public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<MethodEntry> {
21 22
22 private String obfName; 23 private String obfName;
23 private String deobfName; 24 private String deobfName;
24 private Signature obfSignature; 25 private MethodDescriptor obfDescriptor;
25 private Map<Integer, ArgumentMapping> arguments; 26 private Map<Integer, LocalVariableMapping> localVariables;
26 private Mappings.EntryModifier modifier; 27 private Mappings.EntryModifier modifier;
27 28
28 public MethodMapping(String obfName, Signature obfSignature) { 29 public MethodMapping(String obfName, MethodDescriptor obfDescriptor) {
29 this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED); 30 this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED);
30 } 31 }
31 32
32 public MethodMapping(String obfName, Signature obfSignature, String deobfName) { 33 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
33 this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); 34 this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED);
34 } 35 }
35 36
36 public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { 37 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) {
37 if (obfName == null) { 38 Preconditions.checkNotNull(obfName, "Method obf name cannot be null");
38 throw new IllegalArgumentException("obf name cannot be null!"); 39 Preconditions.checkNotNull(obfDescriptor, "Method obf desc cannot be null");
39 }
40 if (obfSignature == null) {
41 throw new IllegalArgumentException("obf signature cannot be null!");
42 }
43 this.obfName = obfName; 40 this.obfName = obfName;
44 this.deobfName = NameValidator.validateMethodName(deobfName); 41 this.deobfName = NameValidator.validateMethodName(deobfName);
45 this.obfSignature = obfSignature; 42 this.obfDescriptor = obfDescriptor;
46 this.arguments = Maps.newTreeMap(); 43 this.localVariables = Maps.newTreeMap();
47 this.modifier = modifier; 44 this.modifier = modifier;
48 } 45 }
49 46
50 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { 47 public MethodMapping(MethodMapping other, Translator translator) {
51 this.obfName = other.obfName; 48 this.obfName = other.obfName;
52 this.deobfName = other.deobfName; 49 this.deobfName = other.deobfName;
53 this.modifier = other.modifier; 50 this.modifier = other.modifier;
54 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); 51 this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor);
55 this.arguments = Maps.newTreeMap(); 52 this.localVariables = Maps.newTreeMap();
56 for (Map.Entry<Integer, ArgumentMapping> entry : other.arguments.entrySet()) { 53 for (Map.Entry<Integer, LocalVariableMapping> entry : other.localVariables.entrySet()) {
57 this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); 54 this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue()));
58 } 55 }
59 } 56 }
60 57
@@ -84,56 +81,56 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
84 this.deobfName = NameValidator.validateMethodName(val); 81 this.deobfName = NameValidator.validateMethodName(val);
85 } 82 }
86 83
87 public Signature getObfSignature() { 84 public MethodDescriptor getObfDesc() {
88 return this.obfSignature; 85 return this.obfDescriptor;
89 } 86 }
90 87
91 public void setObfSignature(Signature val) { 88 public void setObfDescriptor(MethodDescriptor val) {
92 this.obfSignature = val; 89 this.obfDescriptor = val;
93 } 90 }
94 91
95 public Iterable<ArgumentMapping> arguments() { 92 public Iterable<LocalVariableMapping> arguments() {
96 return this.arguments.values(); 93 return this.localVariables.values();
97 } 94 }
98 95
99 public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { 96 public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict {
100 if (this.arguments.containsKey(argumentMapping.getIndex())) { 97 if (this.localVariables.containsKey(localVariableMapping.getIndex())) {
101 throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); 98 throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName());
102 } 99 }
103 this.arguments.put(argumentMapping.getIndex(), argumentMapping); 100 this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping);
104 } 101 }
105 102
106 public String getObfArgumentName(int index) { 103 public String getObfLocalVariableName(int index) {
107 ArgumentMapping argumentMapping = this.arguments.get(index); 104 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
108 if (argumentMapping != null) { 105 if (localVariableMapping != null) {
109 return argumentMapping.getName(); 106 return localVariableMapping.getName();
110 } 107 }
111 108
112 return null; 109 return null;
113 } 110 }
114 111
115 public String getDeobfArgumentName(int index) { 112 public String getDeobfLocalVariableName(int index) {
116 ArgumentMapping argumentMapping = this.arguments.get(index); 113 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
117 if (argumentMapping != null) { 114 if (localVariableMapping != null) {
118 return argumentMapping.getName(); 115 return localVariableMapping.getName();
119 } 116 }
120 117
121 return null; 118 return null;
122 } 119 }
123 120
124 public void setArgumentName(int index, String name) { 121 public void setLocalVariableName(int index, String name) {
125 ArgumentMapping argumentMapping = this.arguments.get(index); 122 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
126 if (argumentMapping == null) { 123 if (localVariableMapping == null) {
127 argumentMapping = new ArgumentMapping(index, name); 124 localVariableMapping = new LocalVariableMapping(index, name);
128 boolean wasAdded = this.arguments.put(index, argumentMapping) == null; 125 boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null;
129 assert (wasAdded); 126 assert (wasAdded);
130 } else { 127 } else {
131 argumentMapping.setName(name); 128 localVariableMapping.setName(name);
132 } 129 }
133 } 130 }
134 131
135 public void removeArgumentName(int index) { 132 public void removeLocalVariableName(int index) {
136 boolean wasRemoved = this.arguments.remove(index) != null; 133 boolean wasRemoved = this.localVariables.remove(index) != null;
137 assert (wasRemoved); 134 assert (wasRemoved);
138 } 135 }
139 136
@@ -146,14 +143,14 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
146 buf.append(this.deobfName); 143 buf.append(this.deobfName);
147 buf.append("\n"); 144 buf.append("\n");
148 buf.append("\t"); 145 buf.append("\t");
149 buf.append(this.obfSignature); 146 buf.append(this.obfDescriptor);
150 buf.append("\n"); 147 buf.append("\n");
151 buf.append("\tArguments:\n"); 148 buf.append("\tLocal Variables:\n");
152 for (ArgumentMapping argumentMapping : this.arguments.values()) { 149 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
153 buf.append("\t\t"); 150 buf.append("\t\t");
154 buf.append(argumentMapping.getIndex()); 151 buf.append(localVariableMapping.getIndex());
155 buf.append(" -> "); 152 buf.append(" -> ");
156 buf.append(argumentMapping.getName()); 153 buf.append(localVariableMapping.getName());
157 buf.append("\n"); 154 buf.append("\n");
158 } 155 }
159 return buf.toString(); 156 return buf.toString();
@@ -161,12 +158,12 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
161 158
162 @Override 159 @Override
163 public int compareTo(MethodMapping other) { 160 public int compareTo(MethodMapping other) {
164 return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); 161 return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor);
165 } 162 }
166 163
167 public boolean containsArgument(String name) { 164 public boolean containsLocalVariable(String name) {
168 for (ArgumentMapping argumentMapping : this.arguments.values()) { 165 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
169 if (argumentMapping.getName().equals(name)) { 166 if (localVariableMapping.getName().equals(name)) {
170 return true; 167 return true;
171 } 168 }
172 } 169 }
@@ -175,32 +172,23 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
175 172
176 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 173 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
177 // rename obf classes in the signature 174 // rename obf classes in the signature
178 Signature newSignature = new Signature(this.obfSignature, className -> 175 MethodDescriptor newDescriptor = obfDescriptor.remap(className -> {
179 {
180 if (className.equals(oldObfClassName)) { 176 if (className.equals(oldObfClassName)) {
181 return newObfClassName; 177 return newObfClassName;
182 } 178 }
183 return null; 179 return className;
184 }); 180 });
185 181
186 if (!newSignature.equals(this.obfSignature)) { 182 if (!newDescriptor.equals(this.obfDescriptor)) {
187 this.obfSignature = newSignature; 183 this.obfDescriptor = newDescriptor;
188 return true; 184 return true;
189 } 185 }
190 return false; 186 return false;
191 } 187 }
192 188
193 public boolean isConstructor() {
194 return this.obfName.startsWith("<");
195 }
196
197 @Override 189 @Override
198 public BehaviorEntry getObfEntry(ClassEntry classEntry) { 190 public MethodEntry getObfEntry(ClassEntry classEntry) {
199 if (isConstructor()) { 191 return new MethodEntry(classEntry, this.obfName, this.obfDescriptor);
200 return new ConstructorEntry(classEntry, this.obfSignature);
201 } else {
202 return new MethodEntry(classEntry, this.obfName, this.obfSignature);
203 }
204 } 192 }
205 193
206 public Mappings.EntryModifier getModifier() { 194 public Mappings.EntryModifier getModifier() {
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
index aa3dc4d..f178093 100644
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
@@ -12,7 +12,6 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.throwables.IllegalNameException; 14import cuchaz.enigma.throwables.IllegalNameException;
15import javassist.bytecode.Descriptor;
16 15
17import java.util.Arrays; 16import java.util.Arrays;
18import java.util.List; 17import java.util.List;
@@ -23,11 +22,11 @@ public class NameValidator {
23 private static final Pattern IdentifierPattern; 22 private static final Pattern IdentifierPattern;
24 private static final Pattern ClassPattern; 23 private static final Pattern ClassPattern;
25 private static final List<String> ReservedWords = Arrays.asList( 24 private static final List<String> ReservedWords = Arrays.asList(
26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", 25 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", 26 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", 27 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", 28 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while" 29 "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
31 ); 30 );
32 31
33 static { 32 static {
@@ -43,10 +42,10 @@ public class NameValidator {
43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { 42 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
44 throw new IllegalNameException(name, "This doesn't look like a legal class name"); 43 throw new IllegalNameException(name, "This doesn't look like a legal class name");
45 } 44 }
46 if (packageRequired && new ClassEntry(name).getPackageName() == null) { 45 if (packageRequired && ClassEntry.getPackageName(name) == null) {
47 throw new IllegalNameException(name, "Class must be in a package"); 46 throw new IllegalNameException(name, "Class must be in a package");
48 } 47 }
49 return Descriptor.toJvmName(name); 48 return name;
50 } 49 }
51 50
52 public static String validateFieldName(String name) { 51 public static String validateFieldName(String name) {
diff --git a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
index 33d930d..9300656 100644
--- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
+++ b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
@@ -12,12 +12,18 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.strobel.assembler.metadata.*; 14import com.strobel.assembler.metadata.*;
15import cuchaz.enigma.bytecode.AccessFlags;
15 16
16import java.util.List; 17import java.util.List;
17 18
18public class ProcyonEntryFactory { 19public class ProcyonEntryFactory {
20 private final ReferencedEntryPool entryPool;
19 21
20 private static String getErasedSignature(MemberReference def) { 22 public ProcyonEntryFactory(ReferencedEntryPool entryPool) {
23 this.entryPool = entryPool;
24 }
25
26 private String getErasedSignature(MemberReference def) {
21 if (!(def instanceof MethodReference)) 27 if (!(def instanceof MethodReference))
22 return def.getErasedSignature(); 28 return def.getErasedSignature();
23 MethodReference methodReference = (MethodReference) def; 29 MethodReference methodReference = (MethodReference) def;
@@ -41,27 +47,23 @@ public class ProcyonEntryFactory {
41 return builder.toString(); 47 return builder.toString();
42 } 48 }
43 49
44 public static FieldEntry getFieldEntry(MemberReference def) { 50 public FieldEntry getFieldEntry(MemberReference def) {
45 return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); 51 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
52 return entryPool.getField(classEntry, def.getName(), def.getErasedSignature());
46 } 53 }
47 54
48 public static MethodEntry getMethodEntry(MemberReference def) { 55 public FieldDefEntry getFieldDefEntry(FieldDefinition def) {
49 return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); 56 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
57 return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), new AccessFlags(def.getModifiers()));
50 } 58 }
51 59
52 public static ConstructorEntry getConstructorEntry(MethodReference def) { 60 public MethodEntry getMethodEntry(MemberReference def) {
53 if (def.isTypeInitializer()) { 61 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
54 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); 62 return entryPool.getMethod(classEntry, def.getName(), getErasedSignature(def));
55 } else {
56 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature()));
57 }
58 } 63 }
59 64
60 public static BehaviorEntry getBehaviorEntry(MethodReference def) { 65 public MethodDefEntry getMethodDefEntry(MethodDefinition def) {
61 if (def.isConstructor() || def.isTypeInitializer()) { 66 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
62 return getConstructorEntry(def); 67 return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), new AccessFlags(def.getModifiers()));
63 } else {
64 return getMethodEntry(def);
65 }
66 } 68 }
67} 69}
diff --git a/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java
new file mode 100644
index 0000000..2abc76c
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java
@@ -0,0 +1,50 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import java.util.HashMap;
15import java.util.Map;
16
17public class ReferencedEntryPool {
18 private final Map<String, ClassEntry> classEntries = new HashMap<>();
19 private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>();
20 private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>();
21
22 public ClassEntry getClass(String name) {
23 return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(name));
24 }
25
26 public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) {
27 return getMethod(ownerEntry, name, new MethodDescriptor(desc));
28 }
29
30 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) {
31 String key = name + desc.toString();
32 return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc));
33 }
34
35 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) {
36 return getField(ownerEntry, name, new TypeDescriptor(desc));
37 }
38
39 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
40 return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc));
41 }
42
43 private Map<String, MethodEntry> getClassMethods(String name) {
44 return methodEntries.computeIfAbsent(name, s -> new HashMap<>());
45 }
46
47 private Map<String, FieldEntry> getClassFields(String name) {
48 return fieldEntries.computeIfAbsent(name, s -> new HashMap<>());
49 }
50}
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java
deleted file mode 100644
index 78130d6..0000000
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ /dev/null
@@ -1,106 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Lists;
15import cuchaz.enigma.utils.Utils;
16
17import java.util.List;
18
19public class Signature {
20
21 private List<Type> argumentTypes;
22 private Type returnType;
23
24 public Signature(String signature) {
25 try {
26 this.argumentTypes = Lists.newArrayList();
27 int i = 0;
28 while (i < signature.length()) {
29 char c = signature.charAt(i);
30 if (c == '(') {
31 assert (this.argumentTypes.isEmpty());
32 assert (this.returnType == null);
33 i++;
34 } else if (c == ')') {
35 i++;
36 break;
37 } else {
38 String type = Type.parseFirst(signature.substring(i));
39 this.argumentTypes.add(new Type(type));
40 i += type.length();
41 }
42 }
43 this.returnType = new Type(Type.parseFirst(signature.substring(i)));
44 } catch (Exception ex) {
45 throw new IllegalArgumentException("Unable to parse signature: " + signature, ex);
46 }
47 }
48
49 public Signature(Signature other, ClassNameReplacer replacer) {
50 this.argumentTypes = Lists.newArrayList(other.argumentTypes);
51 for (int i = 0; i < this.argumentTypes.size(); i++) {
52 this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer));
53 }
54 this.returnType = new Type(other.returnType, replacer);
55 }
56
57 public List<Type> getArgumentTypes() {
58 return this.argumentTypes;
59 }
60
61 public Type getReturnType() {
62 return this.returnType;
63 }
64
65 @Override
66 public String toString() {
67 StringBuilder buf = new StringBuilder();
68 buf.append("(");
69 for (Type type : this.argumentTypes) {
70 buf.append(type);
71 }
72 buf.append(")");
73 buf.append(this.returnType);
74 return buf.toString();
75 }
76
77 public Iterable<Type> types() {
78 List<Type> types = Lists.newArrayList();
79 types.addAll(this.argumentTypes);
80 types.add(this.returnType);
81 return types;
82 }
83
84 @Override
85 public boolean equals(Object other) {
86 return other instanceof Signature && equals((Signature) other);
87 }
88
89 public boolean equals(Signature other) {
90 return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType);
91 }
92
93 @Override
94 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode());
96 }
97
98 public boolean hasClass(ClassEntry classEntry) {
99 for (Type type : types()) {
100 if (type.hasClass() && type.getClassEntry().equals(classEntry)) {
101 return true;
102 }
103 }
104 return false;
105 }
106}
diff --git a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
index 17e3187..4bbde54 100644
--- a/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
+++ b/src/main/java/cuchaz/enigma/mapping/TranslationDirection.java
@@ -13,15 +13,21 @@ package cuchaz.enigma.mapping;
13 13
14public enum TranslationDirection { 14public enum TranslationDirection {
15 15
16 Deobfuscating { 16 DEOBFUSCATING {
17 @Override 17 @Override
18 public <T> T choose(T deobfChoice, T obfChoice) { 18 public <T> T choose(T deobfChoice, T obfChoice) {
19 if (deobfChoice == null) {
20 return obfChoice;
21 }
19 return deobfChoice; 22 return deobfChoice;
20 } 23 }
21 }, 24 },
22 Obfuscating { 25 OBFUSCATING {
23 @Override 26 @Override
24 public <T> T choose(T deobfChoice, T obfChoice) { 27 public <T> T choose(T deobfChoice, T obfChoice) {
28 if (obfChoice == null) {
29 return deobfChoice;
30 }
25 return obfChoice; 31 return obfChoice;
26 } 32 }
27 }; 33 };
diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java
index 8d464fc..59bdf1a 100644
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ b/src/main/java/cuchaz/enigma/mapping/Translator.java
@@ -11,332 +11,50 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Lists; 14public interface Translator {
15import com.google.common.collect.Maps; 15 ClassEntry getTranslatedClass(ClassEntry entry);
16import cuchaz.enigma.analysis.TranslationIndex;
17 16
18import java.util.List; 17 ClassDefEntry getTranslatedClassDef(ClassDefEntry entry);
19import java.util.Map;
20 18
21public class Translator { 19 FieldEntry getTranslatedField(FieldEntry entry);
22 20
23 private TranslationDirection direction; 21 FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry);
24 private Map<String, ClassMapping> classes;
25 private TranslationIndex index;
26 22
27 private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); 23 MethodEntry getTranslatedMethod(MethodEntry entry);
28 24
29 public Translator() { 25 MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry);
30 this.direction = null;
31 this.classes = Maps.newHashMap();
32 this.index = new TranslationIndex();
33 }
34 26
35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { 27 LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry);
36 this.direction = direction;
37 this.classes = classes;
38 this.index = index;
39 }
40 28
41 public TranslationDirection getDirection() { 29 LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry);
42 return direction;
43 }
44 30
45 public TranslationIndex getTranslationIndex() { 31 TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc);
46 return index;
47 }
48 32
49 @SuppressWarnings("unchecked") 33 MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor);
50 public <T extends Entry> T translateEntry(T entry) {
51 if (entry instanceof ClassEntry) {
52 return (T) translateEntry((ClassEntry) entry);
53 } else if (entry instanceof FieldEntry) {
54 return (T) translateEntry((FieldEntry) entry);
55 } else if (entry instanceof MethodEntry) {
56 return (T) translateEntry((MethodEntry) entry);
57 } else if (entry instanceof ConstructorEntry) {
58 return (T) translateEntry((ConstructorEntry) entry);
59 } else if (entry instanceof ArgumentEntry) {
60 return (T) translateEntry((ArgumentEntry) entry);
61 } else if (entry instanceof LocalVariableEntry) {
62 return (T) translateEntry((LocalVariableEntry) entry);
63 } else {
64 throw new Error("Unknown entry type: " + entry.getClass().getName());
65 }
66 }
67 34
68 public <T extends Entry> String translate(T entry) { 35 @SuppressWarnings("unchecked")
69 if (entry instanceof ClassEntry) { 36 default <T extends Entry> T getTranslatedEntry(T entry) {
70 return translate((ClassEntry) entry); 37 if (entry instanceof ClassDefEntry) {
38 return (T) getTranslatedClassDef((ClassDefEntry) entry);
39 } else if (entry instanceof ClassEntry) {
40 return (T) getTranslatedClass((ClassEntry) entry);
41 } else if (entry instanceof FieldDefEntry) {
42 return (T) getTranslatedFieldDef((FieldDefEntry) entry);
43 } else if (entry instanceof MethodDefEntry) {
44 return (T) getTranslatedMethodDef((MethodDefEntry) entry);
71 } else if (entry instanceof FieldEntry) { 45 } else if (entry instanceof FieldEntry) {
72 return translate((FieldEntry) entry); 46 return (T) getTranslatedField((FieldEntry) entry);
73 } else if (entry instanceof MethodEntry) { 47 } else if (entry instanceof MethodEntry) {
74 return translate((MethodEntry) entry); 48 return (T) getTranslatedMethod((MethodEntry) entry);
75 } else if (entry instanceof ConstructorEntry) { 49 } else if (entry instanceof LocalVariableDefEntry) {
76 return translate(entry); 50 return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry);
77 } else if (entry instanceof ArgumentEntry) {
78 return translate((ArgumentEntry) entry);
79 } else if (entry instanceof LocalVariableEntry) { 51 } else if (entry instanceof LocalVariableEntry) {
80 return translate((LocalVariableEntry) entry); 52 return (T) getTranslatedVariable((LocalVariableEntry) entry);
81 } else { 53 } else if (entry instanceof TypeDescriptor) {
82 throw new Error("Unknown entry type: " + entry.getClass().getName()); 54 return (T) getTranslatedTypeDesc((TypeDescriptor) entry);
83 } 55 } else if (entry instanceof MethodDescriptor) {
84 } 56 return (T) getTranslatedMethodDesc((MethodDescriptor) entry);
85
86 public String translate(LocalVariableEntry in) {
87 LocalVariableEntry translated = translateEntry(in);
88 if (translated.equals(in)) {
89 return null;
90 }
91 return translated.getName();
92 }
93
94 public LocalVariableEntry translateEntry(LocalVariableEntry in) {
95 // TODO: Implement it
96 return in;
97 }
98
99 public String translate(ClassEntry in) {
100 ClassEntry translated = translateEntry(in);
101 if (translated.equals(in)) {
102 return null;
103 }
104 return translated.getName();
105 }
106
107 public String translateClass(String className) {
108 return translate(new ClassEntry(className));
109 }
110
111 public ClassEntry translateEntry(ClassEntry in) {
112
113 if (in.isInnerClass()) {
114
115 // translate as much of the class chain as we can
116 List<ClassMapping> mappingsChain = getClassMappingChain(in);
117 String[] obfClassNames = in.getName().split("\\$");
118 StringBuilder buf = new StringBuilder();
119 for (int i = 0; i < obfClassNames.length; i++) {
120 boolean isFirstClass = buf.length() == 0;
121 String className = null;
122 ClassMapping classMapping = mappingsChain.get(i);
123 if (classMapping != null) {
124 className = this.direction.choose(
125 classMapping.getDeobfName(),
126 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
127 );
128 }
129 if (className == null) {
130 className = obfClassNames[i];
131 }
132 if (!isFirstClass) {
133 buf.append("$");
134 }
135 buf.append(className);
136 }
137 return new ClassEntry(buf.toString());
138
139 } else {
140
141 // normal classes are easy
142 ClassMapping classMapping = this.classes.get(in.getName());
143 if (classMapping == null) {
144 return in;
145 }
146 return this.direction.choose(
147 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in,
148 new ClassEntry(classMapping.getObfFullName())
149 );
150 }
151 }
152
153 public String translate(FieldEntry in) {
154
155 // resolve the class entry
156 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in);
157 if (resolvedClassEntry != null) {
158
159 // look for the class
160 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
161 if (classMapping != null) {
162
163 // look for the field
164 String translatedName = this.direction.choose(
165 classMapping.getDeobfFieldName(in.getName(), in.getType()),
166 classMapping.getObfFieldName(in.getName(), translateType(in.getType()))
167 );
168 if (translatedName != null) {
169 return translatedName;
170 }
171 }
172 }
173 return null;
174 }
175
176 public FieldEntry translateEntry(FieldEntry in) {
177 String name = translate(in);
178 if (name == null) {
179 name = in.getName();
180 }
181 return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType()));
182 }
183
184 public String translate(MethodEntry in) {
185 // resolve the class entry
186 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true);
187 if (resolvedClassEntry != null) {
188
189 // look for class
190 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
191 if (classMapping != null) {
192
193 // look for the method
194 MethodMapping methodMapping = this.direction.choose(
195 classMapping.getMethodByObf(in.getName(), in.getSignature()),
196 classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature()))
197 );
198 if (methodMapping != null) {
199 return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName());
200 }
201 }
202 }
203 return null;
204 }
205
206 public MethodEntry translateEntry(MethodEntry in) {
207 String name = translate(in);
208 if (name == null) {
209 name = in.getName();
210 }
211 return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature()));
212 }
213
214 public ConstructorEntry translateEntry(ConstructorEntry in) {
215 if (in.isStatic()) {
216 return new ConstructorEntry(translateEntry(in.getClassEntry()));
217 } else {
218 return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature()));
219 }
220 }
221
222 public BehaviorEntry translateEntry(BehaviorEntry in) {
223 if (in instanceof MethodEntry) {
224 return translateEntry((MethodEntry) in);
225 } else if (in instanceof ConstructorEntry) {
226 return translateEntry((ConstructorEntry) in);
227 }
228 throw new Error("Wrong entry type!");
229 }
230
231 // TODO: support not identical behavior (specific to constructor)
232 public String translate(ArgumentEntry in) {
233 String classTranslate = translateArgument(in);
234
235 // Not found in this class
236 if (classTranslate == null) {
237 List<ClassEntry> ancestry = this.index.getAncestry(in.getClassEntry());
238
239 // Check in mother class for the arg
240 for (ClassEntry entry : ancestry) {
241 ArgumentEntry motherArg = in.cloneToNewClass(entry);
242 if (this.index.entryExists(motherArg)) {
243 String result = translateArgument(motherArg);
244 if (result != null)
245 return result;
246 }
247 }
248 }
249 return classTranslate;
250 }
251
252 public String translateArgument(ArgumentEntry in) {
253 // look for identical behavior in superclasses
254 ClassEntry entry = in.getClassEntry();
255
256 if (entry != null) {
257 // look for the class
258 ClassMapping classMapping = findClassMapping(entry);
259 if (classMapping != null) {
260
261 // look for the method
262 MethodMapping methodMapping = this.direction.choose(
263 classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()),
264 classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature()))
265 );
266 if (methodMapping != null) {
267 return this.direction.choose(
268 methodMapping.getDeobfArgumentName(in.getIndex()),
269 methodMapping.getObfArgumentName(in.getIndex())
270 );
271 }
272 }
273 }
274 return null;
275 }
276
277 public ArgumentEntry translateEntry(ArgumentEntry in) {
278 String name = translate(in);
279 if (name == null) {
280 name = in.getName();
281 }
282 return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name);
283 }
284
285 public Type translateType(Type type) {
286 return new Type(type, this.classNameReplacer);
287 }
288
289 public Signature translateSignature(Signature signature) {
290 return new Signature(signature, this.classNameReplacer);
291 }
292
293 private ClassMapping findClassMapping(ClassEntry in) {
294 List<ClassMapping> mappingChain = getClassMappingChain(in);
295 return mappingChain.get(mappingChain.size() - 1);
296 }
297
298 private List<ClassMapping> getClassMappingChain(ClassEntry in) {
299
300 // get a list of all the classes in the hierarchy
301 String[] parts = in.getName().split("\\$");
302 List<ClassMapping> mappingsChain = Lists.newArrayList();
303
304 // get mappings for the outer class
305 ClassMapping outerClassMapping = this.classes.get(parts[0]);
306 mappingsChain.add(outerClassMapping);
307
308 for (int i = 1; i < parts.length; i++) {
309
310 // get mappings for the inner class
311 ClassMapping innerClassMapping = null;
312 if (outerClassMapping != null) {
313 innerClassMapping = this.direction.choose(
314 outerClassMapping.getInnerClassByObfSimple(parts[i]),
315 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
316 );
317 }
318 mappingsChain.add(innerClassMapping);
319 outerClassMapping = innerClassMapping;
320 }
321
322 assert (mappingsChain.size() == parts.length);
323 return mappingsChain;
324 }
325
326 public Mappings.EntryModifier getModifier(Entry entry) {
327 ClassMapping classMapping = findClassMapping(entry.getClassEntry());
328 if (classMapping != null && !entry.getName().equals("<clinit>")) {
329 if (entry instanceof ClassEntry)
330 return classMapping.getModifier();
331 else if (entry instanceof FieldEntry) {
332 FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType());
333 return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
334 } else if (entry instanceof BehaviorEntry) {
335 MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature());
336 return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED;
337 } else
338 throw new Error("Unknown entry type: " + entry.getClass().getName());
339 } 57 }
340 return Mappings.EntryModifier.UNCHANGED; 58 throw new IllegalArgumentException("Cannot translate unknown entry type");
341 } 59 }
342} 60}
diff --git a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
new file mode 100644
index 0000000..9c0fe6d
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
@@ -0,0 +1,240 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 * <p>
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11
12package cuchaz.enigma.mapping;
13
14import com.google.common.collect.Maps;
15
16import java.util.Map;
17import java.util.function.Function;
18
19public class TypeDescriptor {
20
21 protected final String desc;
22
23 public TypeDescriptor(String desc) {
24 // don't deal with generics
25 // this is just for raw jvm types
26 if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) {
27 throw new IllegalArgumentException("don't use with generic types or templates: " + desc);
28 }
29
30 this.desc = desc;
31 }
32
33 public static String parseFirst(String in) {
34
35 if (in == null || in.length() <= 0) {
36 throw new IllegalArgumentException("No desc to parse, input is empty!");
37 }
38
39 // read one desc from the input
40
41 char c = in.charAt(0);
42
43 // first check for void
44 if (c == 'V') {
45 return "V";
46 }
47
48 // then check for primitives
49 Primitive primitive = Primitive.get(c);
50 if (primitive != null) {
51 return in.substring(0, 1);
52 }
53
54 // then check for classes
55 if (c == 'L') {
56 return readClass(in);
57 }
58
59 // then check for templates
60 if (c == 'T') {
61 return readClass(in);
62 }
63
64 // then check for arrays
65 int dim = countArrayDimension(in);
66 if (dim > 0) {
67 String arrayType = TypeDescriptor.parseFirst(in.substring(dim));
68 return in.substring(0, dim + arrayType.length());
69 }
70
71 throw new IllegalArgumentException("don't know how to parse: " + in);
72 }
73
74 private static int countArrayDimension(String in) {
75 int i = 0;
76 while (i < in.length() && in.charAt(i) == '[')
77 i++;
78 return i;
79 }
80
81 private static String readClass(String in) {
82 // read all the characters in the buffer until we hit a ';'
83 // include the parameters too
84 StringBuilder buf = new StringBuilder();
85 int depth = 0;
86 for (int i = 0; i < in.length(); i++) {
87 char c = in.charAt(i);
88 buf.append(c);
89
90 if (c == '<') {
91 depth++;
92 } else if (c == '>') {
93 depth--;
94 } else if (depth == 0 && c == ';') {
95 return buf.toString();
96 }
97 }
98 return null;
99 }
100
101 public static TypeDescriptor of(String name) {
102 return new TypeDescriptor("L" + name + ";");
103 }
104
105 @Override
106 public String toString() {
107 return this.desc;
108 }
109
110 public boolean isVoid() {
111 return this.desc.length() == 1 && this.desc.charAt(0) == 'V';
112 }
113
114 public boolean isPrimitive() {
115 return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null;
116 }
117
118 public Primitive getPrimitive() {
119 if (!isPrimitive()) {
120 throw new IllegalStateException("not a primitive");
121 }
122 return Primitive.get(this.desc.charAt(0));
123 }
124
125 public boolean isType() {
126 return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';';
127 }
128
129 public ClassEntry getOwnerEntry() {
130 if (isType()) {
131 String name = this.desc.substring(1, this.desc.length() - 1);
132
133 int pos = name.indexOf('<');
134 if (pos >= 0) {
135 // remove the parameters from the class name
136 name = name.substring(0, pos);
137 }
138
139 return new ClassEntry(name);
140
141 } else if (isArray() && getArrayType().isType()) {
142 return getArrayType().getOwnerEntry();
143 } else {
144 throw new IllegalStateException("desc doesn't have a class");
145 }
146 }
147
148 public boolean isArray() {
149 return this.desc.charAt(0) == '[';
150 }
151
152 public int getArrayDimension() {
153 if (!isArray()) {
154 throw new IllegalStateException("not an array");
155 }
156 return countArrayDimension(this.desc);
157 }
158
159 public TypeDescriptor getArrayType() {
160 if (!isArray()) {
161 throw new IllegalStateException("not an array");
162 }
163 return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length()));
164 }
165
166 public boolean containsType() {
167 return isType() || (isArray() && getArrayType().containsType());
168 }
169
170 @Override
171 public boolean equals(Object other) {
172 return other instanceof TypeDescriptor && equals((TypeDescriptor) other);
173 }
174
175 public boolean equals(TypeDescriptor other) {
176 return this.desc.equals(other.desc);
177 }
178
179 @Override
180 public int hashCode() {
181 return this.desc.hashCode();
182 }
183
184 public TypeDescriptor remap(Function<String, String> remapper) {
185 String desc = this.desc;
186 if (isType() || (isArray() && containsType())) {
187 String replacedName = remapper.apply(this.getOwnerEntry().getName());
188 if (replacedName != null) {
189 if (this.isType()) {
190 desc = "L" + replacedName + ";";
191 } else {
192 desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";";
193 }
194 }
195 }
196 return new TypeDescriptor(desc);
197 }
198
199 private static String getArrayPrefix(int dimension) {
200 StringBuilder buf = new StringBuilder();
201 for (int i = 0; i < dimension; i++) {
202 buf.append("[");
203 }
204 return buf.toString();
205 }
206
207 public enum Primitive {
208 Byte('B'),
209 Character('C'),
210 Short('S'),
211 Integer('I'),
212 Long('J'),
213 Float('F'),
214 Double('D'),
215 Boolean('Z');
216
217 private static final Map<Character, Primitive> lookup;
218
219 static {
220 lookup = Maps.newTreeMap();
221 for (Primitive val : values()) {
222 lookup.put(val.getCode(), val);
223 }
224 }
225
226 private char code;
227
228 Primitive(char code) {
229 this.code = code;
230 }
231
232 public static Primitive get(char code) {
233 return lookup.get(code);
234 }
235
236 public char getCode() {
237 return this.code;
238 }
239 }
240}