summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/mapping
diff options
context:
space:
mode:
authorGravatar Modmuss502018-07-18 13:46:00 +0100
committerGravatar GitHub2018-07-18 13:46:00 +0100
commit1ebe691c12f68beea378b133ddc4bcbde7f3f795 (patch)
treefb051d9fde5644bd144a7e9d7bcecc70a256359c /src/main/java/cuchaz/enigma/mapping
parentRecursively rebuild method names (diff)
parentUpdate version number (diff)
downloadenigma-fork-1ebe691c12f68beea378b133ddc4bcbde7f3f795.tar.gz
enigma-fork-1ebe691c12f68beea378b133ddc4bcbde7f3f795.tar.xz
enigma-fork-1ebe691c12f68beea378b133ddc4bcbde7f3f795.zip
Merge pull request #62 from OpenModLoader/asm
ASM based class translator
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/BehaviorEntry.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ClassMapping.java210
-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.java370
-rw-r--r--src/main/java/cuchaz/enigma/mapping/EntryFactory.java132
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldEntry.java87
-rw-r--r--src/main/java/cuchaz/enigma/mapping/FieldMapping.java38
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java102
-rw-r--r--src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java (renamed from src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java)15
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Mappings.java58
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsChecker.java14
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java167
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java124
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java164
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java7
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java7
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MemberMapping.java3
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java114
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodEntry.java90
-rw-r--r--src/main/java/cuchaz/enigma/mapping/MethodMapping.java139
-rw-r--r--src/main/java/cuchaz/enigma/mapping/NameValidator.java16
-rw-r--r--src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java67
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Signature.java128
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TranslationDirection.java10
-rw-r--r--src/main/java/cuchaz/enigma/mapping/Translator.java365
-rw-r--r--src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java (renamed from src/main/java/cuchaz/enigma/mapping/Type.java)117
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java37
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java (renamed from src/main/java/cuchaz/enigma/mapping/ClassEntry.java)32
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/Entry.java (renamed from src/main/java/cuchaz/enigma/mapping/Entry.java)6
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java49
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java43
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java77
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java57
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java82
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java54
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java80
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java48
-rw-r--r--src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java53
40 files changed, 1802 insertions, 1607 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/BehaviorEntry.java
deleted file mode 100644
index 04b4ebc..0000000
--- a/src/main/java/cuchaz/enigma/mapping/BehaviorEntry.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 BehaviorEntry extends Entry {
15 Signature getSignature();
16}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
index 3935de5..8f3f2b2 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java
@@ -12,6 +12,9 @@
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Maps; 14import com.google.common.collect.Maps;
15import cuchaz.enigma.mapping.entry.ClassEntry;
16import cuchaz.enigma.mapping.entry.FieldEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
15import cuchaz.enigma.throwables.MappingConflict; 18import cuchaz.enigma.throwables.MappingConflict;
16 19
17import java.util.ArrayList; 20import java.util.ArrayList;
@@ -34,7 +37,6 @@ public class ClassMapping implements Comparable<ClassMapping> {
34 private Map<String, MethodMapping> methodsByDeobf; 37 private Map<String, MethodMapping> methodsByDeobf;
35 private boolean isDirty; 38 private boolean isDirty;
36 private Mappings.EntryModifier modifier; 39 private Mappings.EntryModifier modifier;
37 private boolean deobfInner;
38 40
39 public ClassMapping(String obfFullName) { 41 public ClassMapping(String obfFullName) {
40 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); 42 this(obfFullName, null, Mappings.EntryModifier.UNCHANGED);
@@ -81,6 +83,10 @@ public class ClassMapping implements Comparable<ClassMapping> {
81 return deobfName; 83 return deobfName;
82 } 84 }
83 85
86 public String getTranslatedName(TranslationDirection direction) {
87 return direction.choose(deobfName, obfFullName);
88 }
89
84 //// INNER CLASSES //////// 90 //// INNER CLASSES ////////
85 91
86 public void setDeobfName(String val) { 92 public void setDeobfName(String val) {
@@ -191,21 +197,21 @@ public class ClassMapping implements Comparable<ClassMapping> {
191 return fieldsByObf.values(); 197 return fieldsByObf.values();
192 } 198 }
193 199
194 public boolean containsObfField(String obfName, Type obfType) { 200 public boolean containsObfField(String obfName, TypeDescriptor obfDesc) {
195 return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); 201 return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc));
196 } 202 }
197 203
198 public boolean containsDeobfField(String deobfName, Type deobfType) { 204 public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) {
199 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); 205 return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc));
200 } 206 }
201 207
202 public void addFieldMapping(FieldMapping fieldMapping) { 208 public void addFieldMapping(FieldMapping fieldMapping) {
203 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 209 String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
204 if (fieldsByObf.containsKey(obfKey)) { 210 if (fieldsByObf.containsKey(obfKey)) {
205 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 211 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
206 } 212 }
207 if (fieldMapping.getDeobfName() != null) { 213 if (fieldMapping.getDeobfName() != null) {
208 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); 214 String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc());
209 if (fieldsByDeobf.containsKey(deobfKey)) { 215 if (fieldsByDeobf.containsKey(deobfKey)) {
210 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 216 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
211 } 217 }
@@ -218,63 +224,67 @@ public class ClassMapping implements Comparable<ClassMapping> {
218 } 224 }
219 225
220 public void removeFieldMapping(FieldMapping fieldMapping) { 226 public void removeFieldMapping(FieldMapping fieldMapping) {
221 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; 227 boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null;
222 assert (obfWasRemoved); 228 assert (obfWasRemoved);
223 if (fieldMapping.getDeobfName() != null) { 229 if (fieldMapping.getDeobfName() != null) {
224 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; 230 boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null;
225 assert (deobfWasRemoved); 231 assert (deobfWasRemoved);
226 } 232 }
227 this.isDirty = true; 233 this.isDirty = true;
228 } 234 }
229 235
230 public FieldMapping getFieldByObf(String obfName, Type obfType) { 236 public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) {
231 return fieldsByObf.get(getFieldKey(obfName, obfType)); 237 return fieldsByObf.get(getFieldKey(obfName, obfDesc));
232 } 238 }
233 239
234 public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { 240 public FieldMapping getFieldByObf(FieldEntry field) {
235 return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 241 return getFieldByObf(field.getName(), field.getDesc());
236 } 242 }
237 243
238 public String getObfFieldName(String deobfName, Type obfType) { 244 public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) {
239 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); 245 return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
246 }
247
248 public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) {
249 FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc));
240 if (fieldMapping != null) { 250 if (fieldMapping != null) {
241 return fieldMapping.getObfName(); 251 return fieldMapping.getObfName();
242 } 252 }
243 return null; 253 return null;
244 } 254 }
245 255
246 public String getDeobfFieldName(String obfName, Type obfType) { 256 public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) {
247 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 257 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
248 if (fieldMapping != null) { 258 if (fieldMapping != null) {
249 return fieldMapping.getDeobfName(); 259 return fieldMapping.getDeobfName();
250 } 260 }
251 return null; 261 return null;
252 } 262 }
253 263
254 private String getFieldKey(String name, Type type) { 264 private String getFieldKey(String name, TypeDescriptor desc) {
255 if (name == null) { 265 if (name == null) {
256 throw new IllegalArgumentException("name cannot be null!"); 266 throw new IllegalArgumentException("name cannot be null!");
257 } 267 }
258 if (type == null) { 268 if (desc == null) {
259 throw new IllegalArgumentException("type cannot be null!"); 269 throw new IllegalArgumentException("desc cannot be null!");
260 } 270 }
261 return name + ":" + type; 271 return name + ":" + desc;
262 } 272 }
263 273
264 public void setFieldName(String obfName, Type obfType, String deobfName) { 274 public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) {
265 assert (deobfName != null); 275 assert (deobfName != null);
266 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); 276 FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc));
267 if (fieldMapping == null) { 277 if (fieldMapping == null) {
268 fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); 278 fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED);
269 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; 279 boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null;
270 assert (obfWasAdded); 280 assert (obfWasAdded);
271 } else { 281 } else {
272 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; 282 boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null;
273 assert (wasRemoved); 283 assert (wasRemoved);
274 } 284 }
275 fieldMapping.setDeobfName(deobfName); 285 fieldMapping.setDeobfName(deobfName);
276 if (deobfName != null) { 286 if (deobfName != null) {
277 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; 287 boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null;
278 assert (wasAdded); 288 assert (wasAdded);
279 } 289 }
280 this.isDirty = true; 290 this.isDirty = true;
@@ -282,13 +292,13 @@ public class ClassMapping implements Comparable<ClassMapping> {
282 292
283 //// METHODS //////// 293 //// METHODS ////////
284 294
285 public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { 295 public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) {
286 assert (newObfName != null); 296 assert (newObfName != null);
287 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); 297 FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc));
288 assert (fieldMapping != null); 298 assert (fieldMapping != null);
289 fieldMapping.setObfName(newObfName); 299 fieldMapping.setObfName(newObfName);
290 fieldMapping.setObfType(newObfType); 300 fieldMapping.setObfDesc(newObfDesc);
291 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; 301 boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null;
292 assert (obfWasAdded); 302 assert (obfWasAdded);
293 this.isDirty = true; 303 this.isDirty = true;
294 } 304 }
@@ -298,23 +308,23 @@ public class ClassMapping implements Comparable<ClassMapping> {
298 return methodsByObf.values(); 308 return methodsByObf.values();
299 } 309 }
300 310
301 public boolean containsObfMethod(String obfName, Signature obfSignature) { 311 public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) {
302 return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); 312 return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor));
303 } 313 }
304 314
305 public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { 315 public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) {
306 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); 316 return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor));
307 } 317 }
308 318
309 public void addMethodMapping(MethodMapping methodMapping) { 319 public void addMethodMapping(MethodMapping methodMapping) {
310 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 320 String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
311 if (methodsByObf.containsKey(obfKey)) { 321 if (methodsByObf.containsKey(obfKey)) {
312 throw new Error("Already have mapping for " + obfFullName + "." + obfKey); 322 throw new Error("Already have mapping for " + obfFullName + "." + obfKey);
313 } 323 }
314 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; 324 boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null;
315 assert (wasAdded); 325 assert (wasAdded);
316 if (methodMapping.getDeobfName() != null) { 326 if (!methodMapping.isObfuscated()) {
317 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); 327 String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc());
318 if (methodsByDeobf.containsKey(deobfKey)) { 328 if (methodsByDeobf.containsKey(deobfKey)) {
319 throw new Error("Already have mapping for " + deobfName + "." + deobfKey); 329 throw new Error("Already have mapping for " + deobfName + "." + deobfKey);
320 } 330 }
@@ -326,44 +336,48 @@ public class ClassMapping implements Comparable<ClassMapping> {
326 } 336 }
327 337
328 public void removeMethodMapping(MethodMapping methodMapping) { 338 public void removeMethodMapping(MethodMapping methodMapping) {
329 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; 339 boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null;
330 assert (obfWasRemoved); 340 assert (obfWasRemoved);
331 if (methodMapping.getDeobfName() != null) { 341 if (!methodMapping.isObfuscated()) {
332 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 342 boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
333 assert (deobfWasRemoved); 343 assert (deobfWasRemoved);
334 } 344 }
335 this.isDirty = true; 345 this.isDirty = true;
336 } 346 }
337 347
338 public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { 348 public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) {
339 return methodsByObf.get(getMethodKey(obfName, obfSignature)); 349 return methodsByObf.get(getMethodKey(obfName, obfDescriptor));
350 }
351
352 public MethodMapping getMethodByObf(MethodEntry method) {
353 return getMethodByObf(method.getName(), method.getDesc());
340 } 354 }
341 355
342 public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { 356 public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) {
343 return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); 357 return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor));
344 } 358 }
345 359
346 private String getMethodKey(String name, Signature signature) { 360 private String getMethodKey(String name, MethodDescriptor descriptor) {
347 if (name == null) { 361 if (name == null) {
348 throw new IllegalArgumentException("name cannot be null!"); 362 throw new IllegalArgumentException("name cannot be null!");
349 } 363 }
350 if (signature == null) { 364 if (descriptor == null) {
351 throw new IllegalArgumentException("signature cannot be null!"); 365 throw new IllegalArgumentException("descriptor cannot be null!");
352 } 366 }
353 return name + signature; 367 return name + descriptor;
354 } 368 }
355 369
356 public void setMethodName(String obfName, Signature obfSignature, String deobfName) { 370 public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
357 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); 371 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor));
358 if (methodMapping == null) { 372 if (methodMapping == null) {
359 methodMapping = createMethodMapping(obfName, obfSignature); 373 methodMapping = createMethodMapping(obfName, obfDescriptor);
360 } else if (methodMapping.getDeobfName() != null) { 374 } else if (!methodMapping.isObfuscated()) {
361 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; 375 boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null;
362 assert (wasRemoved); 376 assert (wasRemoved);
363 } 377 }
364 methodMapping.setDeobfName(deobfName); 378 methodMapping.setDeobfName(deobfName);
365 if (deobfName != null) { 379 if (deobfName != null) {
366 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; 380 boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null;
367 assert (wasAdded); 381 assert (wasAdded);
368 } 382 }
369 this.isDirty = true; 383 this.isDirty = true;
@@ -371,35 +385,35 @@ public class ClassMapping implements Comparable<ClassMapping> {
371 385
372 //// ARGUMENTS //////// 386 //// ARGUMENTS ////////
373 387
374 public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { 388 public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) {
375 assert (newObfName != null); 389 assert (newObfName != null);
376 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); 390 MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor));
377 assert (methodMapping != null); 391 assert (methodMapping != null);
378 methodMapping.setObfName(newObfName); 392 methodMapping.setObfName(newObfName);
379 methodMapping.setObfSignature(newObfSignature); 393 methodMapping.setObfDescriptor(newObfDescriptor);
380 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; 394 boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null;
381 assert (obfWasAdded); 395 assert (obfWasAdded);
382 this.isDirty = true; 396 this.isDirty = true;
383 } 397 }
384 398
385 public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { 399 public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) {
386 assert (argumentName != null); 400 assert (argumentName != null);
387 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); 401 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor));
388 if (methodMapping == null) { 402 if (methodMapping == null) {
389 methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); 403 methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor);
390 } 404 }
391 methodMapping.setArgumentName(argumentIndex, argumentName); 405 methodMapping.setLocalVariableName(argumentIndex, argumentName);
392 this.isDirty = true; 406 this.isDirty = true;
393 } 407 }
394 408
395 public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { 409 public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) {
396 methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); 410 methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex);
397 this.isDirty = true; 411 this.isDirty = true;
398 } 412 }
399 413
400 private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { 414 private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) {
401 MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); 415 MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor);
402 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; 416 boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null;
403 assert (wasAdded); 417 assert (wasAdded);
404 this.isDirty = true; 418 this.isDirty = true;
405 return methodMapping; 419 return methodMapping;
@@ -459,24 +473,24 @@ public class ClassMapping implements Comparable<ClassMapping> {
459 473
460 // rename field types 474 // rename field types
461 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { 475 for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) {
462 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); 476 String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc());
463 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { 477 if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) {
464 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; 478 boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null;
465 assert (wasRemoved); 479 assert (wasRemoved);
466 boolean wasAdded = fieldsByObf 480 boolean wasAdded = fieldsByObf
467 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; 481 .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null;
468 assert (wasAdded); 482 assert (wasAdded);
469 } 483 }
470 } 484 }
471 485
472 // rename method signatures 486 // rename method signatures
473 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { 487 for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) {
474 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); 488 String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc());
475 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { 489 if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) {
476 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; 490 boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null;
477 assert (wasRemoved); 491 assert (wasRemoved);
478 boolean wasAdded = methodsByObf 492 boolean wasAdded = methodsByObf
479 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; 493 .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null;
480 assert (wasAdded); 494 assert (wasAdded);
481 } 495 }
482 } 496 }
@@ -490,9 +504,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
490 return false; 504 return false;
491 } 505 }
492 506
493 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 507 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
494 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); 508 MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc()));
495 return methodMapping != null && methodMapping.containsArgument(name); 509 return methodMapping != null && methodMapping.containsLocalVariable(name);
496 } 510 }
497 511
498 public ClassEntry getObfEntry() { 512 public ClassEntry getObfEntry() {
@@ -503,6 +517,14 @@ public class ClassMapping implements Comparable<ClassMapping> {
503 return deobfFullName != null ? new ClassEntry(deobfFullName) : null; 517 return deobfFullName != null ? new ClassEntry(deobfFullName) : null;
504 } 518 }
505 519
520 public boolean isObfuscated() {
521 return this.deobfName == null || this.deobfName.equals(this.obfFullName);
522 }
523
524 public String getSaveName() {
525 return this.isObfuscated() ? this.obfFullName : this.deobfName;
526 }
527
506 public boolean isDirty() { 528 public boolean isDirty() {
507 return isDirty; 529 return isDirty;
508 } 530 }
@@ -511,8 +533,8 @@ public class ClassMapping implements Comparable<ClassMapping> {
511 this.isDirty = false; 533 this.isDirty = false;
512 } 534 }
513 535
514 public void markDirty(){ 536 public void markDirty() {
515 isDirty = true; 537 this.isDirty = true;
516 } 538 }
517 539
518 public Mappings.EntryModifier getModifier() { 540 public Mappings.EntryModifier getModifier() {
@@ -525,9 +547,9 @@ public class ClassMapping implements Comparable<ClassMapping> {
525 this.modifier = modifier; 547 this.modifier = modifier;
526 } 548 }
527 549
528 public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { 550 public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) {
529 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), 551 FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc),
530 k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); 552 k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED));
531 553
532 if (fieldMapping.getModifier() != modifier) { 554 if (fieldMapping.getModifier() != modifier) {
533 fieldMapping.setModifier(modifier); 555 fieldMapping.setModifier(modifier);
@@ -535,7 +557,7 @@ public class ClassMapping implements Comparable<ClassMapping> {
535 } 557 }
536 } 558 }
537 559
538 public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { 560 public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) {
539 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), 561 MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig),
540 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); 562 k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED));
541 563
@@ -550,4 +572,30 @@ public class ClassMapping implements Comparable<ClassMapping> {
550 this.deobfFullName = deobName; 572 this.deobfFullName = deobName;
551 return this; 573 return this;
552 } 574 }
575
576 public ClassMapping copy() {
577 ClassMapping copied = new ClassMapping(this.obfFullName);
578 copied.obfSimpleName= this.obfSimpleName;
579 copied.modifier = this.modifier;
580 copied.deobfFullName = this.deobfFullName;
581 copied.deobfName = this.deobfName;
582 copied.innerClassesByDeobf = this.innerClassesByDeobf;
583 copied.innerClassesByObfFull = this.innerClassesByObfFull;
584 copied.innerClassesByObfSimple = this.innerClassesByObfSimple;
585 copied.fieldsByObf = this.fieldsByObf;
586 copied.fieldsByDeobf = this.fieldsByDeobf;
587 copied.methodsByObf = this.methodsByObf;
588 copied.methodsByDeobf = this.methodsByDeobf;
589 return copied;
590 }
591
592 @Override
593 public int hashCode() {
594 return this.obfFullName.hashCode();
595 }
596
597 @Override
598 public boolean equals(Object obj) {
599 return obj instanceof ClassMapping && ((ClassMapping) obj).obfFullName.equals(this.obfFullName);
600 }
553} 601}
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..b0bb129
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java
@@ -0,0 +1,370 @@
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;
18import cuchaz.enigma.mapping.entry.*;
19
20import java.util.ArrayList;
21import java.util.List;
22import java.util.Map;
23
24public class DirectionalTranslator implements Translator {
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;
52 if (entry.isArray()) {
53 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
54 } else {
55 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
56 }
57 return new ClassEntry(className);
58 }
59
60 @Override
61 public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) {
62 String className;
63 if (entry.isArray()) {
64 className = this.getTranslatedTypeDesc(new TypeDescriptor(entry.getName())).toString();
65 } else {
66 className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry);
67 }
68 Signature translatedSignature = this.getTranslatedSignature(entry.getSignature());
69 return new ClassDefEntry(className, translatedSignature, getClassModifier(entry).transform(entry.getAccess()));
70 }
71
72 private String translateClassName(ClassEntry entry) {
73 // normal classes are easy
74 ClassMapping classMapping = this.classes.get(entry.getName());
75 if (classMapping == null) {
76 return entry.getName();
77 }
78 return classMapping.getTranslatedName(direction);
79 }
80
81 private String translateInnerClassName(ClassEntry entry) {
82 // translate as much of the class chain as we can
83 List<ClassMapping> mappingsChain = getClassMappingChain(entry);
84 String[] obfClassNames = entry.getName().split("\\$");
85 StringBuilder buf = new StringBuilder();
86 for (int i = 0; i < obfClassNames.length; i++) {
87 boolean isFirstClass = buf.length() == 0;
88 String className = null;
89 ClassMapping classMapping = mappingsChain.get(i);
90 if (classMapping != null) {
91 className = this.direction.choose(
92 classMapping.getDeobfName(),
93 isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName()
94 );
95 }
96 if (className == null) {
97 className = obfClassNames[i];
98 }
99 if (!isFirstClass) {
100 buf.append("$");
101 }
102 buf.append(className);
103 }
104 return buf.toString();
105 }
106
107 @Override
108 public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) {
109 String translatedName = translateFieldName(entry);
110 if (translatedName == null) {
111 translatedName = entry.getName();
112 }
113 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
114 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
115 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
116 AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess());
117 return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, translatedAccess);
118 }
119
120 @Override
121 public FieldEntry getTranslatedField(FieldEntry entry) {
122 String translatedName = translateFieldName(entry);
123 if (translatedName == null) {
124 translatedName = entry.getName();
125 }
126 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
127 TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc());
128 return new FieldEntry(translatedOwner, translatedName, translatedDesc);
129 }
130
131 private String translateFieldName(FieldEntry entry) {
132 // resolve the class entry
133 ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry, true);
134 if (resolvedClassEntry != null) {
135 // look for the class
136 ClassMapping classMapping = findClassMapping(resolvedClassEntry);
137 if (classMapping != null) {
138 // look for the field
139 FieldMapping mapping = this.direction.choose(
140 classMapping.getFieldByObf(entry.getName(), entry.getDesc()),
141 classMapping.getFieldByDeobf(entry.getName(), getTranslatedTypeDesc(entry.getDesc()))
142 );
143 if (mapping != null) {
144 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
145 }
146 }
147 }
148 return null;
149 }
150
151 @Override
152 public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) {
153 String translatedName = translateMethodName(entry);
154 if (translatedName == null) {
155 translatedName = entry.getName();
156 }
157 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
158 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
159 Signature translatedSignature = getTranslatedSignature(entry.getSignature());
160 AccessFlags access = getMethodModifier(entry).transform(entry.getAccess());
161 return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, translatedSignature, access);
162 }
163
164 @Override
165 public MethodEntry getTranslatedMethod(MethodEntry entry) {
166 String translatedName = translateMethodName(entry);
167 if (translatedName == null) {
168 translatedName = entry.getName();
169 }
170 ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry());
171 MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc());
172 return new MethodEntry(translatedOwner, translatedName, translatedDesc);
173 }
174
175 private String translateMethodName(MethodEntry entry) {
176 // resolve the class entry
177 ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry, true);
178 if (resolvedOwner != null) {
179 // look for class
180 ClassMapping classMapping = findClassMapping(resolvedOwner);
181 if (classMapping != null) {
182 // look for the method
183 MethodMapping mapping = this.direction.choose(
184 classMapping.getMethodByObf(entry.getName(), entry.getDesc()),
185 classMapping.getMethodByDeobf(entry.getName(), getTranslatedMethodDesc(entry.getDesc()))
186 );
187 if (mapping != null) {
188 return this.direction.choose(mapping.getDeobfName(), mapping.getObfName());
189 }
190 }
191 }
192 return null;
193 }
194
195 @Override
196 public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) {
197 String translatedArgumentName = translateLocalVariableName(entry);
198 if (translatedArgumentName == null) {
199 translatedArgumentName = inheritLocalVariableName(entry);
200 }
201 if (translatedArgumentName == null) {
202 translatedArgumentName = entry.getName();
203 }
204 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
205 MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry());
206 return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName);
207 }
208
209 @Override
210 public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) {
211 String translatedArgumentName = translateLocalVariableName(entry);
212 if (translatedArgumentName == null) {
213 translatedArgumentName = inheritLocalVariableName(entry);
214 }
215 // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this?
216 MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry());
217 TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc());
218 return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName != null ? translatedArgumentName : entry.getName(), translatedTypeDesc);
219 }
220
221 @Override
222 public boolean hasClassMapping(ClassEntry entry) {
223 return classes.containsKey(entry.getName());
224 }
225
226 @Override
227 public boolean hasFieldMapping(FieldEntry entry) {
228 return translateFieldName(entry) != null;
229 }
230
231 @Override
232 public boolean hasMethodMapping(MethodEntry entry) {
233 return translateMethodName(entry) != null;
234 }
235
236 @Override
237 public boolean hasLocalVariableMapping(LocalVariableEntry entry) {
238 return translateLocalVariableName(entry) != null || inheritLocalVariableName(entry) != null;
239 }
240
241 // TODO: support not identical behavior (specific to constructor)
242 private String translateLocalVariableName(LocalVariableEntry entry) {
243 // look for identical behavior in superclasses
244 ClassEntry ownerEntry = entry.getOwnerClassEntry();
245 if (ownerEntry != null) {
246 // look for the class
247 ClassMapping classMapping = findClassMapping(ownerEntry);
248 if (classMapping != null) {
249 // look for the method
250 MethodMapping methodMapping = this.direction.choose(
251 classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc()),
252 classMapping.getMethodByDeobf(entry.getMethodName(), getTranslatedMethodDesc(entry.getMethodDesc()))
253 );
254 if (methodMapping != null) {
255 int index = entry.getIndex();
256 return this.direction.choose(
257 methodMapping.getDeobfLocalVariableName(index),
258 methodMapping.getObfLocalVariableName(index)
259 );
260 }
261 }
262 }
263 return null;
264 }
265
266 private String inheritLocalVariableName(LocalVariableEntry entry) {
267 List<ClassEntry> ancestry = this.index.getAncestry(entry.getOwnerClassEntry());
268 // Check in mother class for the arg
269 for (ClassEntry ancestorEntry : ancestry) {
270 LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry);
271 if (this.index.entryExists(motherArg)) {
272 String result = translateLocalVariableName(motherArg);
273 if (result != null) {
274 return result;
275 }
276 }
277 }
278 return null;
279 }
280
281 @Override
282 public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) {
283 return desc.remap(this::remapClass);
284 }
285
286 @Override
287 public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) {
288 List<TypeDescriptor> arguments = descriptor.getArgumentDescs();
289 List<TypeDescriptor> translatedArguments = new ArrayList<>(arguments.size());
290 for (TypeDescriptor argument : arguments) {
291 translatedArguments.add(getTranslatedTypeDesc(argument));
292 }
293 return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc()));
294 }
295
296 @Override
297 public Signature getTranslatedSignature(Signature signature) {
298 if (signature == null) {
299 return null;
300 }
301 return signature.remap(this::remapClass);
302 }
303
304 private ClassMapping findClassMapping(ClassEntry entry) {
305 List<ClassMapping> mappingChain = getClassMappingChain(entry);
306 return mappingChain.get(mappingChain.size() - 1);
307 }
308
309 private List<ClassMapping> getClassMappingChain(ClassEntry entry) {
310
311 // get a list of all the classes in the hierarchy
312 String[] parts = entry.getName().split("\\$");
313 List<ClassMapping> mappingsChain = Lists.newArrayList();
314
315 // get mappings for the outer class
316 ClassMapping outerClassMapping = this.classes.get(parts[0]);
317 mappingsChain.add(outerClassMapping);
318
319 for (int i = 1; i < parts.length; i++) {
320
321 // get mappings for the inner class
322 ClassMapping innerClassMapping = null;
323 if (outerClassMapping != null) {
324 innerClassMapping = this.direction.choose(
325 outerClassMapping.getInnerClassByObfSimple(parts[i]),
326 outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i])
327 );
328 }
329 mappingsChain.add(innerClassMapping);
330 outerClassMapping = innerClassMapping;
331 }
332
333 assert (mappingsChain.size() == parts.length);
334 return mappingsChain;
335 }
336
337 private Mappings.EntryModifier getClassModifier(ClassEntry entry) {
338 ClassMapping classMapping = findClassMapping(entry);
339 if (classMapping != null) {
340 return classMapping.getModifier();
341 }
342 return Mappings.EntryModifier.UNCHANGED;
343 }
344
345 private Mappings.EntryModifier getFieldModifier(FieldEntry entry) {
346 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
347 if (classMapping != null) {
348 FieldMapping fieldMapping = classMapping.getFieldByObf(entry);
349 if (fieldMapping != null) {
350 return fieldMapping.getModifier();
351 }
352 }
353 return Mappings.EntryModifier.UNCHANGED;
354 }
355
356 private Mappings.EntryModifier getMethodModifier(MethodEntry entry) {
357 ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry());
358 if (classMapping != null) {
359 MethodMapping methodMapping = classMapping.getMethodByObf(entry);
360 if (methodMapping != null) {
361 return methodMapping.getModifier();
362 }
363 }
364 return Mappings.EntryModifier.UNCHANGED;
365 }
366
367 private String remapClass(String name) {
368 return getTranslatedClass(new ClassEntry(name)).getName();
369 }
370}
diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
deleted file mode 100644
index 993bb64..0000000
--- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java
+++ /dev/null
@@ -1,132 +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.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
22public 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) {
29 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
30 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
31 }
32
33 private static ClassEntry getObfClassEntry(ClassMapping classMapping) {
34 return new ClassEntry(classMapping.getObfFullName());
35 }
36
37 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) {
38 return new ClassEntry(classMapping.getDeobfName());
39 }
40
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) {
58 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType());
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 }
116
117 public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) {
118 if (behaviorName.equals("<clinit>")) {
119 return new ConstructorEntry(classEntry);
120 } else {
121 throw new IllegalArgumentException("Only class initializers don't have signatures");
122 }
123 }
124
125 public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) {
126 return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature());
127 }
128
129 public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) {
130 return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping);
131 }
132}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
deleted file mode 100644
index 0f1f506..0000000
--- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java
+++ /dev/null
@@ -1,87 +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 FieldEntry implements Entry {
17
18 private ClassEntry classEntry;
19 private String name;
20 private Type type;
21
22 // NOTE: this argument order is important for the MethodReader/MethodWriter
23 public FieldEntry(ClassEntry classEntry, String name, Type type) {
24 if (classEntry == null) {
25 throw new IllegalArgumentException("Class cannot be null!");
26 }
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
34 this.classEntry = classEntry;
35 this.name = name;
36 this.type = type;
37 }
38
39 public FieldEntry(FieldEntry other, ClassEntry newClassEntry) {
40 this.classEntry = newClassEntry;
41 this.name = other.name;
42 this.type = other.type;
43 }
44
45 @Override
46 public ClassEntry getClassEntry() {
47 return this.classEntry;
48 }
49
50 @Override
51 public String getName() {
52 return this.name;
53 }
54
55 @Override
56 public String getClassName() {
57 return this.classEntry.getName();
58 }
59
60 public Type getType() {
61 return this.type;
62 }
63
64 @Override
65 public FieldEntry cloneToNewClass(ClassEntry classEntry) {
66 return new FieldEntry(this, classEntry);
67 }
68
69 @Override
70 public int hashCode() {
71 return Utils.combineHashesOrdered(this.classEntry, this.name, this.type);
72 }
73
74 @Override
75 public boolean equals(Object other) {
76 return other instanceof FieldEntry && equals((FieldEntry) other);
77 }
78
79 public boolean equals(FieldEntry other) {
80 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type);
81 }
82
83 @Override
84 public String toString() {
85 return this.classEntry.getName() + "." + this.name + ":" + this.type;
86 }
87}
diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
index cd761b4..8fbe095 100644
--- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java
@@ -11,32 +11,27 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.FieldEntry;
14import cuchaz.enigma.throwables.IllegalNameException; 16import cuchaz.enigma.throwables.IllegalNameException;
15 17
16public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> { 18public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<FieldEntry> {
17 19
18 private String obfName; 20 private String obfName;
19 private String deobfName; 21 private String deobfName;
20 private Type obfType; 22 private TypeDescriptor obfDesc;
21 private Mappings.EntryModifier modifier; 23 private Mappings.EntryModifier modifier;
22 24
23 public FieldMapping(String obfName, Type obfType, String deobfName, Mappings.EntryModifier modifier) { 25 public FieldMapping(String obfName, TypeDescriptor obfDesc, String deobfName, Mappings.EntryModifier modifier) {
24 this.obfName = obfName; 26 this.obfName = obfName;
25 this.deobfName = NameValidator.validateFieldName(deobfName); 27 this.deobfName = NameValidator.validateFieldName(deobfName);
26 this.obfType = obfType; 28 this.obfDesc = obfDesc;
27 this.modifier = modifier; 29 this.modifier = modifier;
28 } 30 }
29 31
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 32 @Override
38 public FieldEntry getObfEntry(ClassEntry classEntry) { 33 public FieldEntry getObfEntry(ClassEntry classEntry) {
39 return new FieldEntry(classEntry, this.obfName, this.obfType); 34 return new FieldEntry(classEntry, this.obfName, this.obfDesc);
40 } 35 }
41 36
42 @Override 37 @Override
@@ -65,12 +60,12 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
65 this.deobfName = NameValidator.validateFieldName(val); 60 this.deobfName = NameValidator.validateFieldName(val);
66 } 61 }
67 62
68 public Type getObfType() { 63 public TypeDescriptor getObfDesc() {
69 return this.obfType; 64 return this.obfDesc;
70 } 65 }
71 66
72 public void setObfType(Type val) { 67 public void setObfDesc(TypeDescriptor val) {
73 this.obfType = val; 68 this.obfDesc = val;
74 } 69 }
75 70
76 public Mappings.EntryModifier getModifier() { 71 public Mappings.EntryModifier getModifier() {
@@ -83,21 +78,20 @@ public class FieldMapping implements Comparable<FieldMapping>, MemberMapping<Fie
83 78
84 @Override 79 @Override
85 public int compareTo(FieldMapping other) { 80 public int compareTo(FieldMapping other) {
86 return (this.obfName + this.obfType).compareTo(other.obfName + other.obfType); 81 return (this.obfName + this.obfDesc).compareTo(other.obfName + other.obfDesc);
87 } 82 }
88 83
89 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 84 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
90 // rename obf classes in the type 85 // rename obf classes in the desc
91 Type newType = new Type(this.obfType, className -> 86 TypeDescriptor newDesc = this.obfDesc.remap(className -> {
92 {
93 if (className.equals(oldObfClassName)) { 87 if (className.equals(oldObfClassName)) {
94 return newObfClassName; 88 return newObfClassName;
95 } 89 }
96 return null; 90 return className;
97 }); 91 });
98 92
99 if (!newType.equals(this.obfType)) { 93 if (!newDesc.equals(this.obfDesc)) {
100 this.obfType = newType; 94 this.obfDesc = newDesc;
101 return true; 95 return true;
102 } 96 }
103 return false; 97 return false;
diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
deleted file mode 100644
index 2bb5e3f..0000000
--- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java
+++ /dev/null
@@ -1,102 +0,0 @@
1package cuchaz.enigma.mapping;
2
3import cuchaz.enigma.utils.Utils;
4
5/**
6 * Desc...
7 * Created by Thog
8 * 19/10/2016
9 */
10public class LocalVariableEntry implements Entry {
11
12 protected final BehaviorEntry behaviorEntry;
13 protected final String name;
14 protected final Type type;
15 protected final int index;
16
17 public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) {
18 if (behaviorEntry == null) {
19 throw new IllegalArgumentException("Behavior cannot be null!");
20 }
21 if (index < 0) {
22 throw new IllegalArgumentException("Index must be non-negative!");
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;
33 this.type = type;
34 this.index = index;
35 }
36
37 public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) {
38 this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry);
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 }
51
52 public int getIndex() {
53 return index;
54 }
55
56 @Override
57 public String getName() {
58 return this.name;
59 }
60
61 @Override
62 public ClassEntry getClassEntry() {
63 return this.behaviorEntry.getClassEntry();
64 }
65
66 @Override
67 public String getClassName() {
68 return this.behaviorEntry.getClassName();
69 }
70
71 @Override
72 public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) {
73 return new LocalVariableEntry(this, classEntry);
74 }
75
76 public String getMethodName() {
77 return this.behaviorEntry.getName();
78 }
79
80 public Signature getMethodSignature() {
81 return this.behaviorEntry.getSignature();
82 }
83
84 @Override
85 public int hashCode() {
86 return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
87 }
88
89 @Override
90 public boolean equals(Object other) {
91 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
92 }
93
94 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;
96 }
97
98 @Override
99 public String toString() {
100 return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")";
101 }
102}
diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
index 91ecd10..62dbcf3 100644
--- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java
@@ -11,18 +11,21 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14public class ArgumentMapping implements Comparable<ArgumentMapping> { 14import cuchaz.enigma.mapping.entry.LocalVariableEntry;
15import cuchaz.enigma.mapping.entry.MethodEntry;
16
17public class LocalVariableMapping implements Comparable<LocalVariableMapping> {
15 18
16 private int index; 19 private int index;
17 private String name; 20 private String name;
18 21
19 // NOTE: this argument order is important for the MethodReader/MethodWriter 22 // NOTE: this argument order is important for the MethodReader/MethodWriter
20 public ArgumentMapping(int index, String name) { 23 public LocalVariableMapping(int index, String name) {
21 this.index = index; 24 this.index = index;
22 this.name = NameValidator.validateArgumentName(name); 25 this.name = NameValidator.validateArgumentName(name);
23 } 26 }
24 27
25 public ArgumentMapping(ArgumentMapping other) { 28 public LocalVariableMapping(LocalVariableMapping other) {
26 this.index = other.index; 29 this.index = other.index;
27 this.name = other.name; 30 this.name = other.name;
28 } 31 }
@@ -39,12 +42,12 @@ public class ArgumentMapping implements Comparable<ArgumentMapping> {
39 this.name = NameValidator.validateArgumentName(val); 42 this.name = NameValidator.validateArgumentName(val);
40 } 43 }
41 44
42 public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { 45 public LocalVariableEntry getObfEntry(MethodEntry methodEntry) {
43 return new ArgumentEntry(behaviorEntry, index, name); 46 return new LocalVariableEntry(methodEntry, index, name);
44 } 47 }
45 48
46 @Override 49 @Override
47 public int compareTo(ArgumentMapping other) { 50 public int compareTo(LocalVariableMapping other) {
48 return Integer.compare(this.index, other.index); 51 return Integer.compare(this.index, other.index);
49 } 52 }
50} 53}
diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java
index cf78ca3..3ef1be5 100644
--- a/src/main/java/cuchaz/enigma/mapping/Mappings.java
+++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java
@@ -15,11 +15,15 @@ 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;
19import cuchaz.enigma.mapping.entry.ClassEntry;
20import cuchaz.enigma.mapping.entry.MethodEntry;
18import cuchaz.enigma.throwables.MappingConflict; 21import cuchaz.enigma.throwables.MappingConflict;
19 22
20import java.io.File; 23import java.io.File;
21import java.io.IOException; 24import java.io.IOException;
22import java.util.*; 25import java.util.*;
26import java.util.stream.Collectors;
23 27
24public class Mappings { 28public class Mappings {
25 29
@@ -96,11 +100,11 @@ public class Mappings {
96 100
97 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { 101 public Translator getTranslator(TranslationDirection direction, TranslationIndex index) {
98 switch (direction) { 102 switch (direction) {
99 case Deobfuscating: 103 case DEOBFUSCATING:
100 104
101 return new Translator(direction, this.classesByObf, index); 105 return new DirectionalTranslator(direction, this.classesByObf, index);
102 106
103 case Obfuscating: 107 case OBFUSCATING:
104 108
105 // fill in the missing deobf class entries with obf entries 109 // fill in the missing deobf class entries with obf entries
106 Map<String, ClassMapping> classes = Maps.newHashMap(); 110 Map<String, ClassMapping> classes = Maps.newHashMap();
@@ -114,9 +118,9 @@ public class Mappings {
114 118
115 // translate the translation index 119 // translate the translation index
116 // NOTE: this isn't actually recursive 120 // NOTE: this isn't actually recursive
117 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); 121 TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index));
118 122
119 return new Translator(direction, classes, deobfIndex); 123 return new DirectionalTranslator(direction, classes, deobfIndex);
120 124
121 default: 125 default:
122 throw new Error("Invalid translation direction!"); 126 throw new Error("Invalid translation direction!");
@@ -151,9 +155,9 @@ public class Mappings {
151 155
152 // add classes from method signatures 156 // add classes from method signatures
153 for (MethodMapping methodMapping : classMapping.methods()) { 157 for (MethodMapping methodMapping : classMapping.methods()) {
154 for (Type type : methodMapping.getObfSignature().types()) { 158 for (TypeDescriptor desc : methodMapping.getObfDesc().types()) {
155 if (type.hasClass()) { 159 if (desc.containsType()) {
156 classNames.add(type.getClassEntry().getClassName()); 160 classNames.add(desc.getTypeEntry().getClassName());
157 } 161 }
158 } 162 }
159 } 163 }
@@ -165,9 +169,9 @@ public class Mappings {
165 return this.classesByDeobf.containsKey(deobfName); 169 return this.classesByDeobf.containsKey(deobfName);
166 } 170 }
167 171
168 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { 172 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) {
169 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 173 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
170 return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); 174 return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc);
171 } 175 }
172 176
173 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { 177 public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) {
@@ -180,14 +184,14 @@ public class Mappings {
180 return false; 184 return false;
181 } 185 }
182 186
183 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { 187 public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) {
184 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); 188 ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName());
185 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); 189 return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor);
186 } 190 }
187 191
188 public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { 192 public boolean containsArgument(MethodEntry obfMethodEntry, String name) {
189 ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); 193 ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName());
190 return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); 194 return classMapping != null && classMapping.containsArgument(obfMethodEntry, name);
191 } 195 }
192 196
193 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) { 197 public List<ClassMapping> getClassMappingChain(ClassEntry obfClass) {
@@ -210,8 +214,14 @@ public class Mappings {
210 214
211 public void savePreviousState() { 215 public void savePreviousState() {
212 this.previousState = new Mappings(this.originMapping); 216 this.previousState = new Mappings(this.originMapping);
213 this.previousState.classesByDeobf = Maps.newHashMap(this.classesByDeobf); 217 this.previousState.classesByDeobf = new HashMap<>();
214 this.previousState.classesByObf = Maps.newHashMap(this.classesByObf); 218 for (Map.Entry<String, ClassMapping> entry : this.classesByDeobf.entrySet()) {
219 this.previousState.classesByDeobf.put(entry.getKey(), entry.getValue().copy());
220 }
221 this.previousState.classesByObf = new HashMap<>();
222 for (Map.Entry<String, ClassMapping> entry : this.classesByObf.entrySet()) {
223 this.previousState.classesByObf.put(entry.getKey(), entry.getValue().copy());
224 }
215 classesByDeobf.values().forEach(ClassMapping::resetDirty); 225 classesByDeobf.values().forEach(ClassMapping::resetDirty);
216 classesByObf.values().forEach(ClassMapping::resetDirty); 226 classesByObf.values().forEach(ClassMapping::resetDirty);
217 } 227 }
@@ -239,5 +249,19 @@ public class Mappings {
239 public String getFormattedName() { 249 public String getFormattedName() {
240 return " ACC:" + super.toString(); 250 return " ACC:" + super.toString();
241 } 251 }
252
253 public AccessFlags transform(AccessFlags access) {
254 switch (this) {
255 case PUBLIC:
256 return access.setPublic();
257 case PROTECTED:
258 return access.setProtected();
259 case PRIVATE:
260 return access.setPrivate();
261 case UNCHANGED:
262 default:
263 return access;
264 }
265 }
242 } 266 }
243} 267}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
index 172641b..a42f255 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java
@@ -14,6 +14,10 @@ package cuchaz.enigma.mapping;
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import com.google.common.collect.Maps; 15import com.google.common.collect.Maps;
16import cuchaz.enigma.analysis.JarIndex; 16import cuchaz.enigma.analysis.JarIndex;
17import cuchaz.enigma.mapping.entry.ClassEntry;
18import cuchaz.enigma.mapping.entry.EntryFactory;
19import cuchaz.enigma.mapping.entry.FieldEntry;
20import cuchaz.enigma.mapping.entry.MethodEntry;
17 21
18import java.util.Map; 22import java.util.Map;
19 23
@@ -23,7 +27,7 @@ public class MappingsChecker {
23 private Map<ClassEntry, ClassMapping> droppedClassMappings; 27 private Map<ClassEntry, ClassMapping> droppedClassMappings;
24 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings; 28 private Map<ClassEntry, ClassMapping> droppedInnerClassMappings;
25 private Map<FieldEntry, FieldMapping> droppedFieldMappings; 29 private Map<FieldEntry, FieldMapping> droppedFieldMappings;
26 private Map<BehaviorEntry, MethodMapping> droppedMethodMappings; 30 private Map<MethodEntry, MethodMapping> droppedMethodMappings;
27 31
28 public MappingsChecker(JarIndex index) { 32 public MappingsChecker(JarIndex index) {
29 this.index = index; 33 this.index = index;
@@ -45,7 +49,7 @@ public class MappingsChecker {
45 return this.droppedFieldMappings; 49 return this.droppedFieldMappings;
46 } 50 }
47 51
48 public Map<BehaviorEntry, MethodMapping> getDroppedMethodMappings() { 52 public Map<MethodEntry, MethodMapping> getDroppedMethodMappings() {
49 return this.droppedMethodMappings; 53 return this.droppedMethodMappings;
50 } 54 }
51 55
@@ -77,10 +81,10 @@ public class MappingsChecker {
77 81
78 // check methods 82 // check methods
79 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { 83 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
80 BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); 84 MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping);
81 if (!this.index.containsObfBehavior(obfBehaviorEntry)) { 85 if (!this.index.containsObfMethod(obfMethodEntry)) {
82 classMapping.removeMethodMapping(methodMapping); 86 classMapping.removeMethodMapping(methodMapping);
83 this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); 87 this.droppedMethodMappings.put(obfMethodEntry, methodMapping);
84 } 88 }
85 } 89 }
86 90
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
index a0d4313..ddbee76 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java
@@ -5,8 +5,14 @@ import com.google.common.collect.Queues;
5import cuchaz.enigma.throwables.MappingConflict; 5import cuchaz.enigma.throwables.MappingConflict;
6import cuchaz.enigma.throwables.MappingParseException; 6import cuchaz.enigma.throwables.MappingParseException;
7 7
8import java.io.*; 8import java.io.BufferedReader;
9import java.io.File;
10import java.io.FileInputStream;
11import java.io.IOException;
12import java.io.InputStream;
13import java.io.InputStreamReader;
9import java.util.Deque; 14import java.util.Deque;
15import java.util.function.Supplier;
10 16
11public class MappingsEnigmaReader { 17public class MappingsEnigmaReader {
12 18
@@ -39,92 +45,95 @@ public class MappingsEnigmaReader {
39 } 45 }
40 46
41 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException { 47 public Mappings readFile(Mappings mappings, File file) throws IOException, MappingParseException {
48 return readFileStream(mappings, new FileInputStream(file), file::getAbsolutePath);
49 }
42 50
43 BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charsets.UTF_8)); 51 public Mappings readFileStream(Mappings mappings, InputStream stream, Supplier<String> filenameSupplier) throws IOException, MappingParseException {
44 Deque<Object> mappingStack = Queues.newArrayDeque(); 52 try (BufferedReader in = new BufferedReader(new InputStreamReader(stream, Charsets.UTF_8))) {
53 Deque<Object> mappingStack = Queues.newArrayDeque();
45 54
46 int lineNumber = 0; 55 int lineNumber = 0;
47 String line; 56 String line;
48 while ((line = in.readLine()) != null) { 57 while ((line = in.readLine()) != null) {
49 lineNumber++; 58 lineNumber++;
50 59
51 // strip comments 60 // strip comments
52 int commentPos = line.indexOf('#'); 61 int commentPos = line.indexOf('#');
53 if (commentPos >= 0) { 62 if (commentPos >= 0) {
54 line = line.substring(0, commentPos); 63 line = line.substring(0, commentPos);
55 } 64 }
56 65
57 // skip blank lines 66 // skip blank lines
58 if (line.trim().length() <= 0) { 67 if (line.trim().length() <= 0) {
59 continue; 68 continue;
60 } 69 }
61 70
62 // get the indent of this line 71 // get the indent of this line
63 int indent = 0; 72 int indent = 0;
64 for (int i = 0; i < line.length(); i++) { 73 for (int i = 0; i < line.length(); i++) {
65 if (line.charAt(i) != '\t') { 74 if (line.charAt(i) != '\t') {
66 break; 75 break;
76 }
77 indent++;
67 } 78 }
68 indent++;
69 }
70 79
71 // handle stack pops 80 // handle stack pops
72 while (indent < mappingStack.size()) { 81 while (indent < mappingStack.size()) {
73 mappingStack.pop(); 82 mappingStack.pop();
74 } 83 }
75 84
76 String[] parts = line.trim().split("\\s"); 85 String[] parts = line.trim().split("\\s");
77 try { 86 try {
78 // read the first token 87 // read the first token
79 String token = parts[0]; 88 String token = parts[0];
80 89
81 if (token.equalsIgnoreCase("CLASS")) { 90 if (token.equalsIgnoreCase("CLASS")) {
82 ClassMapping classMapping; 91 ClassMapping classMapping;
83 if (indent <= 0) { 92 if (indent <= 0) {
84 // outer class 93 // outer class
85 classMapping = readClass(parts, false); 94 classMapping = readClass(parts, false);
86 mappings.addClassMapping(classMapping); 95 mappings.addClassMapping(classMapping);
87 } else { 96 } else {
88 97
89 // inner class 98 // inner class
90 if (!(mappingStack.peek() instanceof ClassMapping)) { 99 if (!(mappingStack.peek() instanceof ClassMapping)) {
91 throw new MappingParseException(file, lineNumber, "Unexpected CLASS entry here!"); 100 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected CLASS entry here!");
101 }
102
103 classMapping = readClass(parts, true);
104 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping);
92 } 105 }
93 106 mappingStack.push(classMapping);
94 classMapping = readClass(parts, true); 107 } else if (token.equalsIgnoreCase("FIELD")) {
95 ((ClassMapping) mappingStack.peek()).addInnerClassMapping(classMapping); 108 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
96 } 109 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected FIELD entry here!");
97 mappingStack.push(classMapping); 110 }
98 } else if (token.equalsIgnoreCase("FIELD")) { 111 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts));
99 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { 112 } else if (token.equalsIgnoreCase("METHOD")) {
100 throw new MappingParseException(file, lineNumber, "Unexpected FIELD entry here!"); 113 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) {
101 } 114 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected METHOD entry here!");
102 ((ClassMapping) mappingStack.peek()).addFieldMapping(readField(parts)); 115 }
103 } else if (token.equalsIgnoreCase("METHOD")) { 116 MethodMapping methodMapping = readMethod(parts);
104 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof ClassMapping)) { 117 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping);
105 throw new MappingParseException(file, lineNumber, "Unexpected METHOD entry here!"); 118 mappingStack.push(methodMapping);
106 } 119 } else if (token.equalsIgnoreCase("ARG")) {
107 MethodMapping methodMapping = readMethod(parts); 120 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) {
108 ((ClassMapping) mappingStack.peek()).addMethodMapping(methodMapping); 121 throw new MappingParseException(filenameSupplier, lineNumber, "Unexpected ARG entry here!");
109 mappingStack.push(methodMapping); 122 }
110 } else if (token.equalsIgnoreCase("ARG")) { 123 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts));
111 if (mappingStack.isEmpty() || !(mappingStack.peek() instanceof MethodMapping)) {
112 throw new MappingParseException(file, lineNumber, "Unexpected ARG entry here!");
113 } 124 }
114 ((MethodMapping) mappingStack.peek()).addArgumentMapping(readArgument(parts)); 125 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
126 throw new MappingParseException(filenameSupplier, lineNumber, "Malformed line:\n" + line);
127 } catch (MappingConflict e) {
128 throw new MappingParseException(filenameSupplier, lineNumber, e.getMessage());
115 } 129 }
116 } catch (ArrayIndexOutOfBoundsException | IllegalArgumentException ex) {
117 throw new MappingParseException(file, lineNumber, "Malformed line:\n" + line);
118 } catch (MappingConflict e) {
119 throw new MappingParseException(file, lineNumber, e.getMessage());
120 } 130 }
131 return mappings;
121 } 132 }
122 in.close();
123 return mappings;
124 } 133 }
125 134
126 private ArgumentMapping readArgument(String[] parts) { 135 private LocalVariableMapping readArgument(String[] parts) {
127 return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); 136 return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]);
128 } 137 }
129 138
130 private ClassMapping readClass(String[] parts, boolean makeSimple) { 139 private ClassMapping readClass(String[] parts, boolean makeSimple) {
@@ -150,27 +159,27 @@ public class MappingsEnigmaReader {
150 if (parts.length == 4) { 159 if (parts.length == 4) {
151 boolean access = parts[3].startsWith("ACC:"); 160 boolean access = parts[3].startsWith("ACC:");
152 if (access) 161 if (access)
153 mapping = new FieldMapping(parts[1], new Type(parts[2]), null, 162 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null,
154 Mappings.EntryModifier.valueOf(parts[3].substring(4))); 163 Mappings.EntryModifier.valueOf(parts[3].substring(4)));
155 else 164 else
156 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); 165 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED);
157 } else if (parts.length == 5) 166 } else if (parts.length == 5)
158 mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); 167 mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4)));
159 return mapping; 168 return mapping;
160 } 169 }
161 170
162 private MethodMapping readMethod(String[] parts) { 171 private MethodMapping readMethod(String[] parts) {
163 MethodMapping mapping = null; 172 MethodMapping mapping = null;
164 if (parts.length == 3) 173 if (parts.length == 3)
165 mapping = new MethodMapping(parts[1], new Signature(parts[2])); 174 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]));
166 else if (parts.length == 4) { 175 else if (parts.length == 4) {
167 boolean access = parts[3].startsWith("ACC:"); 176 boolean access = parts[3].startsWith("ACC:");
168 if (access) 177 if (access)
169 mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); 178 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4)));
170 else 179 else
171 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); 180 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]);
172 } else if (parts.length == 5) 181 } else if (parts.length == 5)
173 mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], 182 mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2],
174 Mappings.EntryModifier.valueOf(parts[4].substring(4))); 183 Mappings.EntryModifier.valueOf(parts[4].substring(4)));
175 return mapping; 184 return mapping;
176 } 185 }
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
index ba1b258..b29990f 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java
@@ -14,9 +14,7 @@ package cuchaz.enigma.mapping;
14import com.google.common.base.Charsets; 14import com.google.common.base.Charsets;
15 15
16import java.io.*; 16import java.io.*;
17import java.util.ArrayList; 17import java.util.*;
18import java.util.Collections;
19import java.util.List;
20 18
21public class MappingsEnigmaWriter { 19public class MappingsEnigmaWriter {
22 20
@@ -33,83 +31,67 @@ public class MappingsEnigmaWriter {
33 if (!target.exists() && !target.mkdirs()) 31 if (!target.exists() && !target.mkdirs())
34 throw new IOException("Cannot create mapping directory!"); 32 throw new IOException("Cannot create mapping directory!");
35 33
34 Mappings previousState = mappings.getPreviousState();
36 for (ClassMapping classMapping : sorted(mappings.classes())) { 35 for (ClassMapping classMapping : sorted(mappings.classes())) {
37 if (!classMapping.isDirty()) 36 if (!classMapping.isDirty()) {
38 continue; 37 continue;
39 this.deletePreviousClassMapping(target, classMapping);
40 File obFile = new File(target, classMapping.getObfFullName() + ".mapping");
41 File result;
42 if (classMapping.getDeobfName() == null)
43 result = obFile;
44 else {
45 // Make sure that old version of the file doesn't exist
46 if (obFile.exists())
47 obFile.delete();
48 result = new File(target, classMapping.getDeobfName() + ".mapping");
49 } 38 }
50 39
51 if (!result.getParentFile().exists()) 40 if (previousState != null) {
52 result.getParentFile().mkdirs(); 41 ClassMapping previousClass = previousState.classesByObf.get(classMapping.getObfFullName());
42 File previousFile;
43 if (previousClass != null) {
44 previousFile = new File(target, previousClass.getSaveName() + ".mapping");
45 } else {
46 previousFile = new File(target, classMapping.getObfFullName() + ".mapping");
47 }
48 if (previousFile.exists() && !previousFile.delete()) {
49 System.err.println("Failed to delete old class mapping " + previousFile.getName());
50 }
51 }
52
53 File result = new File(target, classMapping.getSaveName() + ".mapping");
54
55 File packageFile = result.getParentFile();
56 if (!packageFile.exists()) {
57 packageFile.mkdirs();
58 }
53 result.createNewFile(); 59 result.createNewFile();
54 PrintWriter outputWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(result), Charsets.UTF_8)); 60
55 write(outputWriter, classMapping, 0); 61 try (PrintWriter outputWriter = new PrintWriter(new BufferedWriter(new FileWriter(result)))) {
56 outputWriter.close(); 62 write(outputWriter, classMapping, 0);
63 }
57 } 64 }
58 65
59 // Remove dropped mappings 66 // Remove dropped mappings
60 if (mappings.getPreviousState() != null) { 67 if (previousState != null) {
61 List<ClassMapping> droppedClassMappings = new ArrayList<>(mappings.getPreviousState().classes()); 68 Set<ClassMapping> droppedClassMappings = new HashSet<>(previousState.classes());
62 List<ClassMapping> classMappings = new ArrayList<>(mappings.classes()); 69 droppedClassMappings.removeAll(mappings.classes());
63 droppedClassMappings.removeAll(classMappings); 70 for (ClassMapping droppedMapping : droppedClassMappings) {
64 for (ClassMapping classMapping : droppedClassMappings) { 71 File result = new File(target, droppedMapping.getSaveName() + ".mapping");
65 File obFile = new File(target, classMapping.getObfFullName() + ".mapping"); 72 if (!result.exists()) {
66 File result; 73 continue;
67 if (classMapping.getDeobfName() == null) 74 }
68 result = obFile; 75 if (!result.delete()) {
69 else { 76 System.err.println("Failed to delete dropped class mapping " + result.getName());
70 // Make sure that old version of the file doesn't exist
71 if (obFile.exists())
72 obFile.delete();
73 result = new File(target, classMapping.getDeobfName() + ".mapping");
74 } 77 }
75 if (result.exists())
76 result.delete();
77 } 78 }
78 } 79 }
79 } 80 }
80 81
81 private void deletePreviousClassMapping(File target, ClassMapping classMapping) {
82 File prevFile = null;
83 // Deob rename
84 if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() != null && !classMapping.getPreviousDeobfName().equals(classMapping.getDeobfName())) {
85 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
86 }
87 // Deob to ob rename
88 else if (classMapping.getDeobfName() == null && classMapping.getPreviousDeobfName() != null) {
89 prevFile = new File(target, classMapping.getPreviousDeobfName() + ".mapping");
90 }
91 // Ob to Deob rename
92 else if (classMapping.getDeobfName() != null && classMapping.getPreviousDeobfName() == null) {
93 prevFile = new File(target, classMapping.getObfFullName() + ".mapping");
94 }
95
96 if (prevFile != null && prevFile.exists())
97 prevFile.delete();
98 }
99
100 public void write(PrintWriter out, Mappings mappings) throws IOException { 82 public void write(PrintWriter out, Mappings mappings) throws IOException {
101 for (ClassMapping classMapping : sorted(mappings.classes())) { 83 for (ClassMapping classMapping : sorted(mappings.classes())) {
102 write(out, classMapping, 0); 84 write(out, classMapping, 0);
103 } 85 }
104 } 86 }
105 87
106 private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { 88 protected void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException {
107 if (classMapping.getDeobfName() == null) { 89 if (classMapping.getDeobfName() == null) {
108 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(), 90 out.format("%sCLASS %s%s\n", getIndent(depth), classMapping.getObfFullName(),
109 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); 91 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
110 } else { 92 } else {
111 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(), 93 out.format("%sCLASS %s %s%s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName(),
112 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName()); 94 classMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : classMapping.getModifier().getFormattedName());
113 } 95 }
114 96
115 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { 97 for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) {
@@ -127,32 +109,32 @@ public class MappingsEnigmaWriter {
127 109
128 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { 110 private void write(PrintWriter out, FieldMapping fieldMapping, int depth) {
129 if (fieldMapping.getDeobfName() == null) 111 if (fieldMapping.getDeobfName() == null)
130 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), 112 out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(),
131 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); 113 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
132 else 114 else
133 out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), 115 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()); 116 fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName());
135 } 117 }
136 118
137 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { 119 private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException {
138 if (methodMapping.getDeobfName() == null) { 120 if (methodMapping.isObfuscated()) {
139 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), 121 out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(),
140 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); 122 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
141 } else { 123 } else {
142 out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), 124 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()); 125 methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName());
144 } 126 }
145 127
146 for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { 128 for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) {
147 write(out, argumentMapping, depth + 1); 129 write(out, localVariableMapping, depth + 1);
148 } 130 }
149 } 131 }
150 132
151 private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { 133 private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) {
152 out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); 134 out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName());
153 } 135 }
154 136
155 private <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) { 137 protected <T extends Comparable<T>> List<T> sorted(Iterable<T> classes) {
156 List<T> out = new ArrayList<>(); 138 List<T> out = new ArrayList<>();
157 for (T t : classes) { 139 for (T t : classes) {
158 out.add(t); 140 out.add(t);
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
index 7126d2b..85b6d2a 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java
@@ -13,6 +13,7 @@ package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Lists; 14import com.google.common.collect.Lists;
15import cuchaz.enigma.analysis.JarIndex; 15import cuchaz.enigma.analysis.JarIndex;
16import cuchaz.enigma.mapping.entry.*;
16import cuchaz.enigma.throwables.IllegalNameException; 17import cuchaz.enigma.throwables.IllegalNameException;
17import cuchaz.enigma.throwables.MappingConflict; 18import cuchaz.enigma.throwables.MappingConflict;
18 19
@@ -25,12 +26,14 @@ import java.util.zip.GZIPOutputStream;
25 26
26public class MappingsRenamer { 27public class MappingsRenamer {
27 28
28 private JarIndex index; 29 private final JarIndex index;
30 private final ReferencedEntryPool entryPool;
29 private Mappings mappings; 31 private Mappings mappings;
30 32
31 public MappingsRenamer(JarIndex index, Mappings mappings) { 33 public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) {
32 this.index = index; 34 this.index = index;
33 this.mappings = mappings; 35 this.mappings = mappings;
36 this.entryPool = entryPool;
34 } 37 }
35 38
36 public void setMappings(Mappings mappings) { 39 public void setMappings(Mappings mappings) {
@@ -46,7 +49,7 @@ public class MappingsRenamer {
46 49
47 if (deobfName != null) { 50 if (deobfName != null) {
48 // make sure we don't rename to an existing obf or deobf class 51 // make sure we don't rename to an existing obf or deobf class
49 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { 52 if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) {
50 throw new IllegalNameException(deobfName, "There is already a class with that name"); 53 throw new IllegalNameException(deobfName, "There is already a class with that name");
51 } 54 }
52 } 55 }
@@ -87,13 +90,13 @@ public class MappingsRenamer {
87 90
88 public void setFieldName(FieldEntry obf, String deobfName) { 91 public void setFieldName(FieldEntry obf, String deobfName) {
89 deobfName = NameValidator.validateFieldName(deobfName); 92 deobfName = NameValidator.validateFieldName(deobfName);
90 FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); 93 FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
91 ClassEntry definedClass = null; 94 ClassEntry definedClass = null;
92 if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) 95 if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry))
93 definedClass = obf.getClassEntry(); 96 definedClass = obf.getOwnerClassEntry();
94 else { 97 else {
95 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { 98 for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) {
96 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { 99 if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) {
97 definedClass = ancestorEntry; 100 definedClass = ancestorEntry;
98 break; 101 break;
99 } 102 }
@@ -101,42 +104,44 @@ public class MappingsRenamer {
101 } 104 }
102 105
103 if (definedClass != null) { 106 if (definedClass != null) {
104 String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); 107 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
108 String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName();
105 if (className == null) 109 if (className == null)
106 className = definedClass.getClassName(); 110 className = definedClass.getClassName();
107 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); 111 throw new IllegalNameException(deobfName, "There is already a field with that name in " + className);
108 } 112 }
109 113
110 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 114 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
111 classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); 115 classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName);
112 } 116 }
113 117
114 public void removeFieldMapping(FieldEntry obf) { 118 public void removeFieldMapping(FieldEntry obf) {
115 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 119 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
116 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); 120 classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc()));
117 } 121 }
118 122
119 public void markFieldAsDeobfuscated(FieldEntry obf) { 123 public void markFieldAsDeobfuscated(FieldEntry obf) {
120 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 124 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
121 classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); 125 classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName());
122 } 126 }
123 127
124 private void validateMethodTreeName(MethodEntry entry, String deobfName) { 128 private void validateMethodTreeName(MethodEntry entry, String deobfName) {
125 MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); 129 MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc());
126 130
127 // TODO: Verify if I don't break things 131 // TODO: Verify if I don't break things
128 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 132 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())) 133 if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc()))
130 || index.containsObfBehavior(targetEntry)) { 134 || index.containsObfMethod(targetEntry)) {
131 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); 135 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
136 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName();
132 if (deobfClassName == null) { 137 if (deobfClassName == null) {
133 deobfClassName = entry.getClassName(); 138 deobfClassName = entry.getClassName();
134 } 139 }
135 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 140 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
136 } 141 }
137 142
138 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { 143 for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) {
139 validateMethodTreeName(entry.cloneToNewClass(child), deobfName); 144 validateMethodTreeName(entry.updateOwnership(child), deobfName);
140 } 145 }
141 } 146 }
142 147
@@ -155,20 +160,21 @@ public class MappingsRenamer {
155 160
156 public void setMethodName(MethodEntry obf, String deobfName) { 161 public void setMethodName(MethodEntry obf, String deobfName) {
157 deobfName = NameValidator.validateMethodName(deobfName); 162 deobfName = NameValidator.validateMethodName(deobfName);
158 MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); 163 MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc());
159 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 164 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
160 165
161 // TODO: Verify if I don't break things 166 // 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())) 167 if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc()))
163 || index.containsObfBehavior(targetEntry)) { 168 || index.containsObfMethod(targetEntry)) {
164 String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); 169 Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex());
170 String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName();
165 if (deobfClassName == null) { 171 if (deobfClassName == null) {
166 deobfClassName = obf.getClassName(); 172 deobfClassName = obf.getClassName();
167 } 173 }
168 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); 174 throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName);
169 } 175 }
170 176
171 classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); 177 classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName);
172 } 178 }
173 179
174 public void removeMethodTreeMapping(MethodEntry obf) { 180 public void removeMethodTreeMapping(MethodEntry obf) {
@@ -176,8 +182,8 @@ public class MappingsRenamer {
176 } 182 }
177 183
178 public void removeMethodMapping(MethodEntry obf) { 184 public void removeMethodMapping(MethodEntry obf) {
179 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 185 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
180 classMapping.setMethodName(obf.getName(), obf.getSignature(), null); 186 classMapping.setMethodName(obf.getName(), obf.getDesc(), null);
181 } 187 }
182 188
183 public void markMethodTreeAsDeobfuscated(MethodEntry obf) { 189 public void markMethodTreeAsDeobfuscated(MethodEntry obf) {
@@ -185,30 +191,25 @@ public class MappingsRenamer {
185 } 191 }
186 192
187 public void markMethodAsDeobfuscated(MethodEntry obf) { 193 public void markMethodAsDeobfuscated(MethodEntry obf) {
188 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 194 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
189 classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); 195 classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName());
190 } 196 }
191 197
192 public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { 198 public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) {
193 if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { 199 MethodEntry obfMethod = obf.getOwnerEntry();
194 setArgumentName(obf, deobfName);
195 return;
196 }
197
198 MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry();
199 200
200 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod); 201 Set<MethodEntry> implementations = index.getRelatedMethodImplementations(obfMethod);
201 for (MethodEntry entry : implementations) { 202 for (MethodEntry entry : implementations) {
202 ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); 203 ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry());
203 if (classMapping != null) { 204 if (classMapping != null) {
204 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); 205 MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc());
205 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 206 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
206 // TODO: Verify if I don't break things 207 // TODO: Verify if I don't break things
207 if (mapping != null) { 208 if (mapping != null) {
208 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 209 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
209 if (argumentMapping.getIndex() != obf.getIndex()) { 210 if (localVariableMapping.getIndex() != obf.getIndex()) {
210 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 211 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
211 || argumentMapping.getName().equals(deobfName)) { 212 || localVariableMapping.getName().equals(deobfName)) {
212 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 213 throw new IllegalNameException(deobfName, "There is already an argument with that name");
213 } 214 }
214 } 215 }
@@ -218,45 +219,45 @@ public class MappingsRenamer {
218 } 219 }
219 220
220 for (MethodEntry entry : implementations) { 221 for (MethodEntry entry : implementations) {
221 setArgumentName(new ArgumentEntry(obf, entry), deobfName); 222 setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName()), deobfName);
222 } 223 }
223 } 224 }
224 225
225 public void setArgumentName(ArgumentEntry obf, String deobfName) { 226 public void setLocalVariableName(LocalVariableEntry obf, String deobfName) {
226 deobfName = NameValidator.validateArgumentName(deobfName); 227 deobfName = NameValidator.validateArgumentName(deobfName);
227 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 228 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
228 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); 229 MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc());
229 // NOTE: don't need to check arguments for name collisions with names determined by Procyon 230 // NOTE: don't need to check arguments for name collisions with names determined by Procyon
230 // TODO: Verify if I don't break things 231 // TODO: Verify if I don't break things
231 if (mapping != null) { 232 if (mapping != null) {
232 for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { 233 for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) {
233 if (argumentMapping.getIndex() != obf.getIndex()) { 234 if (localVariableMapping.getIndex() != obf.getIndex()) {
234 if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) 235 if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName)
235 || argumentMapping.getName().equals(deobfName)) { 236 || localVariableMapping.getName().equals(deobfName)) {
236 throw new IllegalNameException(deobfName, "There is already an argument with that name"); 237 throw new IllegalNameException(deobfName, "There is already an argument with that name");
237 } 238 }
238 } 239 }
239 } 240 }
240 } 241 }
241 242
242 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); 243 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName);
243 } 244 }
244 245
245 public void removeArgumentMapping(ArgumentEntry obf) { 246 public void removeLocalVariableMapping(LocalVariableEntry obf) {
246 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 247 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
247 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); 248 classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex());
248 } 249 }
249 250
250 public void markArgumentAsDeobfuscated(ArgumentEntry obf) { 251 public void markArgumentAsDeobfuscated(LocalVariableEntry obf) {
251 ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); 252 ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry());
252 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); 253 classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName());
253 } 254 }
254 255
255 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { 256 public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) {
256 classMapping.removeFieldMapping(fieldMapping); 257 classMapping.removeFieldMapping(fieldMapping);
257 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 258 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
258 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { 259 if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) {
259 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { 260 if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) {
260 targetClassMapping.addFieldMapping(fieldMapping); 261 targetClassMapping.addFieldMapping(fieldMapping);
261 return true; 262 return true;
262 } else { 263 } else {
@@ -269,12 +270,12 @@ public class MappingsRenamer {
269 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { 270 public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) {
270 classMapping.removeMethodMapping(methodMapping); 271 classMapping.removeMethodMapping(methodMapping);
271 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); 272 ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass);
272 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { 273 if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) {
273 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { 274 if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) {
274 targetClassMapping.addMethodMapping(methodMapping); 275 targetClassMapping.addMethodMapping(methodMapping);
275 return true; 276 return true;
276 } else { 277 } else {
277 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); 278 System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc());
278 } 279 }
279 } 280 }
280 return false; 281 return false;
@@ -326,12 +327,35 @@ public class MappingsRenamer {
326 } 327 }
327 328
328 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { 329 public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) {
329 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 330 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
330 classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); 331 classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier);
332 }
333
334 public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) {
335 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry());
336 classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier);
337 }
338
339 public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) {
340 ClassMapping classMapping = getOrCreateClassMapping(obfEntry);
341 return classMapping.getModifier();
331 } 342 }
332 343
333 public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) { 344 public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) {
334 ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); 345 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
335 classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); 346 FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry);
347 if (fieldMapping == null) {
348 return Mappings.EntryModifier.UNCHANGED;
349 }
350 return fieldMapping.getModifier();
351 }
352
353 public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) {
354 ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry());
355 MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry);
356 if (methodMapping == null) {
357 return Mappings.EntryModifier.UNCHANGED;
358 }
359 return methodMapping.getModifier();
336 } 360 }
337} 361}
diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
index b0eb826..32f0ee9 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java
@@ -2,6 +2,7 @@ package cuchaz.enigma.mapping;
2 2
3import com.google.common.base.Charsets; 3import com.google.common.base.Charsets;
4import cuchaz.enigma.analysis.TranslationIndex; 4import cuchaz.enigma.analysis.TranslationIndex;
5import cuchaz.enigma.mapping.entry.ReferencedEntryPool;
5 6
6import java.io.*; 7import java.io.*;
7import java.util.ArrayList; 8import java.util.ArrayList;
@@ -19,7 +20,7 @@ public class MappingsSRGWriter {
19 } 20 }
20 file.createNewFile(); 21 file.createNewFile();
21 22
22 TranslationIndex index = new TranslationIndex(); 23 TranslationIndex index = new TranslationIndex(new ReferencedEntryPool());
23 24
24 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); 25 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8));
25 List<String> fieldMappings = new ArrayList<>(); 26 List<String> fieldMappings = new ArrayList<>();
@@ -43,7 +44,7 @@ public class MappingsSRGWriter {
43 } 44 }
44 45
45 for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { 46 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())); 47 methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
47 } 48 }
48 } 49 }
49 50
@@ -52,7 +53,7 @@ public class MappingsSRGWriter {
52 } 53 }
53 54
54 for (MethodMapping methodMapping : sorted(classMapping.methods())) { 55 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())); 56 methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc()));
56 } 57 }
57 } 58 }
58 for (String fd : fieldMappings) { 59 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 befc92a..69d5684 100644
--- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
+++ b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java
@@ -2,6 +2,7 @@ package cuchaz.enigma.mapping;
2 2
3import com.google.common.base.Charsets; 3import com.google.common.base.Charsets;
4import com.google.common.collect.Maps; 4import com.google.common.collect.Maps;
5import cuchaz.enigma.mapping.entry.ClassEntry;
5import cuchaz.enigma.throwables.MappingConflict; 6import cuchaz.enigma.throwables.MappingConflict;
6import cuchaz.enigma.throwables.MappingParseException; 7import cuchaz.enigma.throwables.MappingParseException;
7 8
@@ -20,11 +21,11 @@ public class MappingsTinyReader {
20 } 21 }
21 22
22 public FieldMapping readField(String[] parts) { 23 public FieldMapping readField(String[] parts) {
23 return new FieldMapping(parts[3], new Type(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); 24 return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED);
24 } 25 }
25 26
26 public MethodMapping readMethod(String[] parts) { 27 public MethodMapping readMethod(String[] parts) {
27 return new MethodMapping(parts[3], new Signature(parts[2]), parts[4]); 28 return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]);
28 } 29 }
29 30
30 public Mappings read(File file) throws IOException, MappingParseException { 31 public Mappings read(File file) throws IOException, MappingParseException {
@@ -72,7 +73,7 @@ public class MappingsTinyReader {
72 break; 73 break;
73 case "MTH-ARG": 74 case "MTH-ARG":
74 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1])); 75 classMapping = classMappingMap.computeIfAbsent(parts[1], k -> new ClassMapping(parts[1]));
75 classMapping.setArgumentName(parts[3], new Signature(parts[2]), Integer.parseInt(parts[4]), parts[5]); 76 classMapping.setArgumentName(parts[3], new MethodDescriptor(parts[2]), Integer.parseInt(parts[4]), parts[5]);
76 break; 77 break;
77 default: 78 default:
78 throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !"); 79 throw new MappingParseException(file, lineNumber, "Unknown token '" + token + "' !");
diff --git a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
index d4514d4..6effb91 100644
--- a/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MemberMapping.java
@@ -11,6 +11,9 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry;
15import cuchaz.enigma.mapping.entry.Entry;
16
14public interface MemberMapping<T extends Entry> { 17public interface MemberMapping<T extends Entry> {
15 T getObfEntry(ClassEntry classEntry); 18 T getObfEntry(ClassEntry classEntry);
16 19
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..0fc0351
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java
@@ -0,0 +1,114 @@
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.mapping.entry.ClassEntry;
16import cuchaz.enigma.utils.Utils;
17
18import java.util.ArrayList;
19import java.util.List;
20import java.util.function.Function;
21
22public class MethodDescriptor {
23
24 private List<TypeDescriptor> argumentDescs;
25 private TypeDescriptor returnDesc;
26
27 public MethodDescriptor(String desc) {
28 try {
29 this.argumentDescs = Lists.newArrayList();
30 int i = 0;
31 while (i < desc.length()) {
32 char c = desc.charAt(i);
33 if (c == '(') {
34 assert (this.argumentDescs.isEmpty());
35 assert (this.returnDesc == null);
36 i++;
37 } else if (c == ')') {
38 i++;
39 break;
40 } else {
41 String type = TypeDescriptor.parseFirst(desc.substring(i));
42 this.argumentDescs.add(new TypeDescriptor(type));
43 i += type.length();
44 }
45 }
46 this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i)));
47 } catch (Exception ex) {
48 throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex);
49 }
50 }
51
52 public MethodDescriptor(List<TypeDescriptor> argumentDescs, TypeDescriptor returnDesc) {
53 this.argumentDescs = argumentDescs;
54 this.returnDesc = returnDesc;
55 }
56
57 public List<TypeDescriptor> getArgumentDescs() {
58 return this.argumentDescs;
59 }
60
61 public TypeDescriptor getReturnDesc() {
62 return this.returnDesc;
63 }
64
65 @Override
66 public String toString() {
67 StringBuilder buf = new StringBuilder();
68 buf.append("(");
69 for (TypeDescriptor desc : this.argumentDescs) {
70 buf.append(desc);
71 }
72 buf.append(")");
73 buf.append(this.returnDesc);
74 return buf.toString();
75 }
76
77 public Iterable<TypeDescriptor> types() {
78 List<TypeDescriptor> descs = Lists.newArrayList();
79 descs.addAll(this.argumentDescs);
80 descs.add(this.returnDesc);
81 return descs;
82 }
83
84 @Override
85 public boolean equals(Object other) {
86 return other instanceof MethodDescriptor && equals((MethodDescriptor) other);
87 }
88
89 public boolean equals(MethodDescriptor other) {
90 return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc);
91 }
92
93 @Override
94 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode());
96 }
97
98 public boolean hasClass(ClassEntry classEntry) {
99 for (TypeDescriptor desc : types()) {
100 if (desc.containsType() && desc.getTypeEntry().equals(classEntry)) {
101 return true;
102 }
103 }
104 return false;
105 }
106
107 public MethodDescriptor remap(Function<String, String> remapper) {
108 List<TypeDescriptor> argumentDescs = new ArrayList<>(this.argumentDescs.size());
109 for (TypeDescriptor desc : this.argumentDescs) {
110 argumentDescs.add(desc.remap(remapper));
111 }
112 return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper));
113 }
114}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
deleted file mode 100644
index 9c3058c..0000000
--- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java
+++ /dev/null
@@ -1,90 +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 MethodEntry implements BehaviorEntry {
17
18 private ClassEntry classEntry;
19 private String name;
20 private Signature signature;
21
22 public MethodEntry(ClassEntry classEntry, String name, Signature signature) {
23 if (classEntry == null) {
24 throw new IllegalArgumentException("Class cannot be null!");
25 }
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
36 this.classEntry = classEntry;
37 this.name = name;
38 this.signature = signature;
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 }
46
47 @Override
48 public ClassEntry getClassEntry() {
49 return this.classEntry;
50 }
51
52 @Override
53 public String getName() {
54 return this.name;
55 }
56
57 @Override
58 public Signature getSignature() {
59 return this.signature;
60 }
61
62 @Override
63 public String getClassName() {
64 return this.classEntry.getName();
65 }
66
67 @Override
68 public MethodEntry cloneToNewClass(ClassEntry classEntry) {
69 return new MethodEntry(this, classEntry.getName());
70 }
71
72 @Override
73 public int hashCode() {
74 return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature);
75 }
76
77 @Override
78 public boolean equals(Object other) {
79 return other instanceof MethodEntry && equals((MethodEntry) other);
80 }
81
82 public boolean equals(MethodEntry other) {
83 return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature);
84 }
85
86 @Override
87 public String toString() {
88 return this.classEntry.getName() + "." + this.name + this.signature;
89 }
90}
diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
index 1524ce6..2f10144 100644
--- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
+++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java
@@ -11,50 +11,49 @@
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;
16import cuchaz.enigma.mapping.entry.ClassEntry;
17import cuchaz.enigma.mapping.entry.MethodEntry;
15import cuchaz.enigma.throwables.IllegalNameException; 18import cuchaz.enigma.throwables.IllegalNameException;
16import cuchaz.enigma.throwables.MappingConflict; 19import cuchaz.enigma.throwables.MappingConflict;
17 20
18import java.util.Map; 21import java.util.Map;
19 22
20public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<BehaviorEntry> { 23public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<MethodEntry> {
21 24
22 private String obfName; 25 private String obfName;
23 private String deobfName; 26 private String deobfName;
24 private Signature obfSignature; 27 private MethodDescriptor obfDescriptor;
25 private Map<Integer, ArgumentMapping> arguments; 28 private Map<Integer, LocalVariableMapping> localVariables;
26 private Mappings.EntryModifier modifier; 29 private Mappings.EntryModifier modifier;
27 30
28 public MethodMapping(String obfName, Signature obfSignature) { 31 public MethodMapping(String obfName, MethodDescriptor obfDescriptor) {
29 this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED); 32 this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED);
30 } 33 }
31 34
32 public MethodMapping(String obfName, Signature obfSignature, String deobfName) { 35 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
33 this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); 36 this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED);
34 } 37 }
35 38
36 public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { 39 public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) {
37 if (obfName == null) { 40 Preconditions.checkNotNull(obfName, "Method obf name cannot be null");
38 throw new IllegalArgumentException("obf name cannot be null!"); 41 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; 42 this.obfName = obfName;
44 this.deobfName = NameValidator.validateMethodName(deobfName); 43 this.deobfName = NameValidator.validateMethodName(deobfName);
45 this.obfSignature = obfSignature; 44 this.obfDescriptor = obfDescriptor;
46 this.arguments = Maps.newTreeMap(); 45 this.localVariables = Maps.newTreeMap();
47 this.modifier = modifier; 46 this.modifier = modifier;
48 } 47 }
49 48
50 public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { 49 public MethodMapping(MethodMapping other, Translator translator) {
51 this.obfName = other.obfName; 50 this.obfName = other.obfName;
52 this.deobfName = other.deobfName; 51 this.deobfName = other.deobfName;
53 this.modifier = other.modifier; 52 this.modifier = other.modifier;
54 this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); 53 this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor);
55 this.arguments = Maps.newTreeMap(); 54 this.localVariables = Maps.newTreeMap();
56 for (Map.Entry<Integer, ArgumentMapping> entry : other.arguments.entrySet()) { 55 for (Map.Entry<Integer, LocalVariableMapping> entry : other.localVariables.entrySet()) {
57 this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); 56 this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue()));
58 } 57 }
59 } 58 }
60 59
@@ -77,6 +76,9 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
77 } 76 }
78 77
79 public String getDeobfName() { 78 public String getDeobfName() {
79 if (deobfName == null) {
80 return obfName;
81 }
80 return this.deobfName; 82 return this.deobfName;
81 } 83 }
82 84
@@ -84,56 +86,56 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
84 this.deobfName = NameValidator.validateMethodName(val); 86 this.deobfName = NameValidator.validateMethodName(val);
85 } 87 }
86 88
87 public Signature getObfSignature() { 89 public MethodDescriptor getObfDesc() {
88 return this.obfSignature; 90 return this.obfDescriptor;
89 } 91 }
90 92
91 public void setObfSignature(Signature val) { 93 public void setObfDescriptor(MethodDescriptor val) {
92 this.obfSignature = val; 94 this.obfDescriptor = val;
93 } 95 }
94 96
95 public Iterable<ArgumentMapping> arguments() { 97 public Iterable<LocalVariableMapping> arguments() {
96 return this.arguments.values(); 98 return this.localVariables.values();
97 } 99 }
98 100
99 public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { 101 public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict {
100 if (this.arguments.containsKey(argumentMapping.getIndex())) { 102 if (this.localVariables.containsKey(localVariableMapping.getIndex())) {
101 throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); 103 throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName());
102 } 104 }
103 this.arguments.put(argumentMapping.getIndex(), argumentMapping); 105 this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping);
104 } 106 }
105 107
106 public String getObfArgumentName(int index) { 108 public String getObfLocalVariableName(int index) {
107 ArgumentMapping argumentMapping = this.arguments.get(index); 109 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
108 if (argumentMapping != null) { 110 if (localVariableMapping != null) {
109 return argumentMapping.getName(); 111 return localVariableMapping.getName();
110 } 112 }
111 113
112 return null; 114 return null;
113 } 115 }
114 116
115 public String getDeobfArgumentName(int index) { 117 public String getDeobfLocalVariableName(int index) {
116 ArgumentMapping argumentMapping = this.arguments.get(index); 118 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
117 if (argumentMapping != null) { 119 if (localVariableMapping != null) {
118 return argumentMapping.getName(); 120 return localVariableMapping.getName();
119 } 121 }
120 122
121 return null; 123 return null;
122 } 124 }
123 125
124 public void setArgumentName(int index, String name) { 126 public void setLocalVariableName(int index, String name) {
125 ArgumentMapping argumentMapping = this.arguments.get(index); 127 LocalVariableMapping localVariableMapping = this.localVariables.get(index);
126 if (argumentMapping == null) { 128 if (localVariableMapping == null) {
127 argumentMapping = new ArgumentMapping(index, name); 129 localVariableMapping = new LocalVariableMapping(index, name);
128 boolean wasAdded = this.arguments.put(index, argumentMapping) == null; 130 boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null;
129 assert (wasAdded); 131 assert (wasAdded);
130 } else { 132 } else {
131 argumentMapping.setName(name); 133 localVariableMapping.setName(name);
132 } 134 }
133 } 135 }
134 136
135 public void removeArgumentName(int index) { 137 public void removeLocalVariableName(int index) {
136 boolean wasRemoved = this.arguments.remove(index) != null; 138 boolean wasRemoved = this.localVariables.remove(index) != null;
137 assert (wasRemoved); 139 assert (wasRemoved);
138 } 140 }
139 141
@@ -146,14 +148,14 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
146 buf.append(this.deobfName); 148 buf.append(this.deobfName);
147 buf.append("\n"); 149 buf.append("\n");
148 buf.append("\t"); 150 buf.append("\t");
149 buf.append(this.obfSignature); 151 buf.append(this.obfDescriptor);
150 buf.append("\n"); 152 buf.append("\n");
151 buf.append("\tArguments:\n"); 153 buf.append("\tLocal Variables:\n");
152 for (ArgumentMapping argumentMapping : this.arguments.values()) { 154 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
153 buf.append("\t\t"); 155 buf.append("\t\t");
154 buf.append(argumentMapping.getIndex()); 156 buf.append(localVariableMapping.getIndex());
155 buf.append(" -> "); 157 buf.append(" -> ");
156 buf.append(argumentMapping.getName()); 158 buf.append(localVariableMapping.getName());
157 buf.append("\n"); 159 buf.append("\n");
158 } 160 }
159 return buf.toString(); 161 return buf.toString();
@@ -161,12 +163,12 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
161 163
162 @Override 164 @Override
163 public int compareTo(MethodMapping other) { 165 public int compareTo(MethodMapping other) {
164 return (this.obfName + this.obfSignature).compareTo(other.obfName + other.obfSignature); 166 return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor);
165 } 167 }
166 168
167 public boolean containsArgument(String name) { 169 public boolean containsLocalVariable(String name) {
168 for (ArgumentMapping argumentMapping : this.arguments.values()) { 170 for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
169 if (argumentMapping.getName().equals(name)) { 171 if (localVariableMapping.getName().equals(name)) {
170 return true; 172 return true;
171 } 173 }
172 } 174 }
@@ -175,32 +177,23 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
175 177
176 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { 178 public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
177 // rename obf classes in the signature 179 // rename obf classes in the signature
178 Signature newSignature = new Signature(this.obfSignature, className -> 180 MethodDescriptor newDescriptor = obfDescriptor.remap(className -> {
179 {
180 if (className.equals(oldObfClassName)) { 181 if (className.equals(oldObfClassName)) {
181 return newObfClassName; 182 return newObfClassName;
182 } 183 }
183 return null; 184 return className;
184 }); 185 });
185 186
186 if (!newSignature.equals(this.obfSignature)) { 187 if (!newDescriptor.equals(this.obfDescriptor)) {
187 this.obfSignature = newSignature; 188 this.obfDescriptor = newDescriptor;
188 return true; 189 return true;
189 } 190 }
190 return false; 191 return false;
191 } 192 }
192 193
193 public boolean isConstructor() {
194 return this.obfName.startsWith("<");
195 }
196
197 @Override 194 @Override
198 public BehaviorEntry getObfEntry(ClassEntry classEntry) { 195 public MethodEntry getObfEntry(ClassEntry classEntry) {
199 if (isConstructor()) { 196 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 } 197 }
205 198
206 public Mappings.EntryModifier getModifier() { 199 public Mappings.EntryModifier getModifier() {
@@ -210,4 +203,8 @@ public class MethodMapping implements Comparable<MethodMapping>, MemberMapping<B
210 public void setModifier(Mappings.EntryModifier modifier) { 203 public void setModifier(Mappings.EntryModifier modifier) {
211 this.modifier = modifier; 204 this.modifier = modifier;
212 } 205 }
206
207 public boolean isObfuscated() {
208 return deobfName == null || deobfName.equals(obfName);
209 }
213} 210}
diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
index aa3dc4d..9273c9b 100644
--- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java
@@ -11,8 +11,8 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import cuchaz.enigma.mapping.entry.ClassEntry;
14import cuchaz.enigma.throwables.IllegalNameException; 15import cuchaz.enigma.throwables.IllegalNameException;
15import javassist.bytecode.Descriptor;
16 16
17import java.util.Arrays; 17import java.util.Arrays;
18import java.util.List; 18import java.util.List;
@@ -23,11 +23,11 @@ public class NameValidator {
23 private static final Pattern IdentifierPattern; 23 private static final Pattern IdentifierPattern;
24 private static final Pattern ClassPattern; 24 private static final Pattern ClassPattern;
25 private static final List<String> ReservedWords = Arrays.asList( 25 private static final List<String> ReservedWords = Arrays.asList(
26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", 26 "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized",
27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", 27 "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte",
28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", 28 "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch",
29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", 29 "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally",
30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while" 30 "long", "strictfp", "volatile", "const", "float", "native", "super", "while"
31 ); 31 );
32 32
33 static { 33 static {
@@ -43,10 +43,10 @@ public class NameValidator {
43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { 43 if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) {
44 throw new IllegalNameException(name, "This doesn't look like a legal class name"); 44 throw new IllegalNameException(name, "This doesn't look like a legal class name");
45 } 45 }
46 if (packageRequired && new ClassEntry(name).getPackageName() == null) { 46 if (packageRequired && ClassEntry.getPackageName(name) == null) {
47 throw new IllegalNameException(name, "Class must be in a package"); 47 throw new IllegalNameException(name, "Class must be in a package");
48 } 48 }
49 return Descriptor.toJvmName(name); 49 return name;
50 } 50 }
51 51
52 public static String validateFieldName(String name) { 52 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
deleted file mode 100644
index 33d930d..0000000
--- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java
+++ /dev/null
@@ -1,67 +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.strobel.assembler.metadata.*;
15
16import java.util.List;
17
18public class ProcyonEntryFactory {
19
20 private static String getErasedSignature(MemberReference def) {
21 if (!(def instanceof MethodReference))
22 return def.getErasedSignature();
23 MethodReference methodReference = (MethodReference) def;
24 StringBuilder builder = new StringBuilder("(");
25 for (ParameterDefinition param : methodReference.getParameters()) {
26 TypeReference paramType = param.getParameterType();
27 if (paramType.getErasedSignature().equals("Ljava/lang/Object;") && paramType.hasExtendsBound() && paramType.getExtendsBound() instanceof CompoundTypeReference) {
28 List<TypeReference> interfaces = ((CompoundTypeReference) paramType.getExtendsBound()).getInterfaces();
29 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature()));
30 } else
31 builder.append(paramType.getErasedSignature());
32 }
33 builder.append(")");
34
35 TypeReference returnType = methodReference.getReturnType();
36 if (returnType.getErasedSignature().equals("Ljava/lang/Object;") && returnType.hasExtendsBound() && returnType.getExtendsBound() instanceof CompoundTypeReference) {
37 List<TypeReference> interfaces = ((CompoundTypeReference) returnType.getExtendsBound()).getInterfaces();
38 interfaces.forEach((inter) -> builder.append(inter.getErasedSignature()));
39 } else
40 builder.append(returnType.getErasedSignature());
41 return builder.toString();
42 }
43
44 public static FieldEntry getFieldEntry(MemberReference def) {
45 return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature()));
46 }
47
48 public static MethodEntry getMethodEntry(MemberReference def) {
49 return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def)));
50 }
51
52 public static ConstructorEntry getConstructorEntry(MethodReference def) {
53 if (def.isTypeInitializer()) {
54 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()));
55 } else {
56 return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature()));
57 }
58 }
59
60 public static BehaviorEntry getBehaviorEntry(MethodReference def) {
61 if (def.isConstructor() || def.isTypeInitializer()) {
62 return getConstructorEntry(def);
63 } else {
64 return getMethodEntry(def);
65 }
66 }
67}
diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java
index 78130d6..071e4af 100644
--- a/src/main/java/cuchaz/enigma/mapping/Signature.java
+++ b/src/main/java/cuchaz/enigma/mapping/Signature.java
@@ -1,106 +1,82 @@
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; 1package cuchaz.enigma.mapping;
13 2
14import com.google.common.collect.Lists; 3import cuchaz.enigma.bytecode.translators.TranslationSignatureVisitor;
15import cuchaz.enigma.utils.Utils; 4import org.objectweb.asm.signature.SignatureReader;
5import org.objectweb.asm.signature.SignatureVisitor;
6import org.objectweb.asm.signature.SignatureWriter;
16 7
17import java.util.List; 8import java.util.function.Function;
9import java.util.regex.Pattern;
18 10
19public class Signature { 11public class Signature {
12 private static final Pattern OBJECT_PATTERN = Pattern.compile(".*:Ljava/lang/Object;:.*");
20 13
21 private List<Type> argumentTypes; 14 private final String signature;
22 private Type returnType; 15 private final boolean isType;
23 16
24 public Signature(String signature) { 17 private Signature(String signature, boolean isType) {
25 try { 18 if (signature != null && OBJECT_PATTERN.matcher(signature).matches()) {
26 this.argumentTypes = Lists.newArrayList(); 19 signature = signature.replaceAll(":Ljava/lang/Object;:", "::");
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 } 20 }
21
22 this.signature = signature;
23 this.isType = isType;
47 } 24 }
48 25
49 public Signature(Signature other, ClassNameReplacer replacer) { 26 public static Signature createTypedSignature(String signature) {
50 this.argumentTypes = Lists.newArrayList(other.argumentTypes); 27 if (signature != null && !signature.isEmpty()) {
51 for (int i = 0; i < this.argumentTypes.size(); i++) { 28 return new Signature(signature, true);
52 this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer));
53 } 29 }
54 this.returnType = new Type(other.returnType, replacer); 30 return new Signature(null, true);
55 } 31 }
56 32
57 public List<Type> getArgumentTypes() { 33 public static Signature createSignature(String signature) {
58 return this.argumentTypes; 34 if (signature != null && !signature.isEmpty()) {
35 return new Signature(signature, false);
36 }
37 return new Signature(null, false);
59 } 38 }
60 39
61 public Type getReturnType() { 40 public String getSignature() {
62 return this.returnType; 41 return signature;
63 } 42 }
64 43
65 @Override 44 public boolean isType() {
66 public String toString() { 45 return isType;
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 } 46 }
76 47
77 public Iterable<Type> types() { 48 public Signature remap(Function<String, String> remapper) {
78 List<Type> types = Lists.newArrayList(); 49 if (signature == null) {
79 types.addAll(this.argumentTypes); 50 return this;
80 types.add(this.returnType); 51 }
81 return types; 52 SignatureWriter writer = new SignatureWriter();
53 SignatureVisitor visitor = new TranslationSignatureVisitor(remapper, writer);
54 if (isType) {
55 new SignatureReader(signature).acceptType(visitor);
56 } else {
57 new SignatureReader(signature).accept(visitor);
58 }
59 return new Signature(writer.toString(), isType);
82 } 60 }
83 61
84 @Override 62 @Override
85 public boolean equals(Object other) { 63 public boolean equals(Object obj) {
86 return other instanceof Signature && equals((Signature) other); 64 if (obj instanceof Signature) {
87 } 65 Signature other = (Signature) obj;
88 66 return (other.signature == null && signature == null || other.signature != null
89 public boolean equals(Signature other) { 67 && signature != null && other.signature.equals(signature))
90 return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); 68 && other.isType == this.isType;
69 }
70 return false;
91 } 71 }
92 72
93 @Override 73 @Override
94 public int hashCode() { 74 public int hashCode() {
95 return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); 75 return signature.hashCode() | (isType ? 1 : 0) << 16;
96 } 76 }
97 77
98 public boolean hasClass(ClassEntry classEntry) { 78 @Override
99 for (Type type : types()) { 79 public String toString() {
100 if (type.hasClass() && type.getClassEntry().equals(classEntry)) { 80 return signature;
101 return true;
102 }
103 }
104 return false;
105 } 81 }
106} 82}
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..a9ff1cb 100644
--- a/src/main/java/cuchaz/enigma/mapping/Translator.java
+++ b/src/main/java/cuchaz/enigma/mapping/Translator.java
@@ -11,332 +11,99 @@
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping;
13 13
14import com.google.common.collect.Lists; 14import cuchaz.enigma.mapping.entry.*;
15import com.google.common.collect.Maps; 15import org.objectweb.asm.Handle;
16import cuchaz.enigma.analysis.TranslationIndex; 16import org.objectweb.asm.Type;
17 17
18import java.util.List; 18public interface Translator {
19import java.util.Map; 19 ClassEntry getTranslatedClass(ClassEntry entry);
20 20
21public class Translator { 21 ClassDefEntry getTranslatedClassDef(ClassDefEntry entry);
22 22
23 private TranslationDirection direction; 23 FieldEntry getTranslatedField(FieldEntry entry);
24 private Map<String, ClassMapping> classes;
25 private TranslationIndex index;
26 24
27 private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); 25 FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry);
28 26
29 public Translator() { 27 MethodEntry getTranslatedMethod(MethodEntry entry);
30 this.direction = null;
31 this.classes = Maps.newHashMap();
32 this.index = new TranslationIndex();
33 }
34 28
35 public Translator(TranslationDirection direction, Map<String, ClassMapping> classes, TranslationIndex index) { 29 MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry);
36 this.direction = direction;
37 this.classes = classes;
38 this.index = index;
39 }
40 30
41 public TranslationDirection getDirection() { 31 LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry);
42 return direction;
43 }
44 32
45 public TranslationIndex getTranslationIndex() { 33 LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry);
46 return index;
47 }
48 34
49 @SuppressWarnings("unchecked") 35 boolean hasClassMapping(ClassEntry entry);
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 36
68 public <T extends Entry> String translate(T entry) { 37 boolean hasFieldMapping(FieldEntry entry);
69 if (entry instanceof ClassEntry) {
70 return translate((ClassEntry) entry);
71 } else if (entry instanceof FieldEntry) {
72 return translate((FieldEntry) entry);
73 } else if (entry instanceof MethodEntry) {
74 return translate((MethodEntry) entry);
75 } else if (entry instanceof ConstructorEntry) {
76 return translate(entry);
77 } else if (entry instanceof ArgumentEntry) {
78 return translate((ArgumentEntry) entry);
79 } else if (entry instanceof LocalVariableEntry) {
80 return translate((LocalVariableEntry) entry);
81 } else {
82 throw new Error("Unknown entry type: " + entry.getClass().getName());
83 }
84 }
85 38
86 public String translate(LocalVariableEntry in) { 39 boolean hasMethodMapping(MethodEntry entry);
87 LocalVariableEntry translated = translateEntry(in);
88 if (translated.equals(in)) {
89 return null;
90 }
91 return translated.getName();
92 }
93 40
94 public LocalVariableEntry translateEntry(LocalVariableEntry in) { 41 boolean hasLocalVariableMapping(LocalVariableEntry entry);
95 // TODO: Implement it
96 return in;
97 }
98 42
99 public String translate(ClassEntry in) { 43 TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc);
100 ClassEntry translated = translateEntry(in);
101 if (translated.equals(in)) {
102 return null;
103 }
104 return translated.getName();
105 }
106 44
107 public String translateClass(String className) { 45 MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor);
108 return translate(new ClassEntry(className));
109 }
110
111 public ClassEntry translateEntry(ClassEntry in) {
112 46
113 if (in.isInnerClass()) { 47 Signature getTranslatedSignature(Signature signature);
114 48
115 // translate as much of the class chain as we can 49 default Type getTranslatedType(Type type) {
116 List<ClassMapping> mappingsChain = getClassMappingChain(in); 50 String descString = type.getDescriptor();
117 String[] obfClassNames = in.getName().split("\\$"); 51 switch (type.getSort()) {
118 StringBuilder buf = new StringBuilder(); 52 case Type.OBJECT: {
119 for (int i = 0; i < obfClassNames.length; i++) { 53 ClassEntry classEntry = new ClassEntry(type.getInternalName());
120 boolean isFirstClass = buf.length() == 0; 54 return Type.getObjectType(getTranslatedClass(classEntry).getName());
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 } 55 }
137 return new ClassEntry(buf.toString()); 56 case Type.ARRAY: {
138 57 TypeDescriptor descriptor = new TypeDescriptor(descString);
139 } else { 58 return Type.getType(getTranslatedTypeDesc(descriptor).toString());
140
141 // normal classes are easy
142 ClassMapping classMapping = this.classes.get(in.getName());
143 if (classMapping == null) {
144 return in;
145 } 59 }
146 return this.direction.choose( 60 case Type.METHOD: {
147 classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, 61 MethodDescriptor descriptor = new MethodDescriptor(descString);
148 new ClassEntry(classMapping.getObfFullName()) 62 return Type.getMethodType(getTranslatedMethodDesc(descriptor).toString());
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 } 63 }
172 } 64 }
173 return null; 65 return type;
174 } 66 }
175 67
176 public FieldEntry translateEntry(FieldEntry in) { 68 default Handle getTranslatedHandle(Handle handle) {
177 String name = translate(in); 69 MethodEntry entry = new MethodEntry(new ClassEntry(handle.getOwner()), handle.getName(), new MethodDescriptor(handle.getDesc()));
178 if (name == null) { 70 MethodEntry translatedMethod = getTranslatedMethod(entry);
179 name = in.getName(); 71 ClassEntry ownerClass = translatedMethod.getOwnerClassEntry();
180 } 72 return new Handle(handle.getTag(), ownerClass.getName(), translatedMethod.getName(), translatedMethod.getDesc().toString(), handle.isInterface());
181 return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType()));
182 } 73 }
183 74
184 public String translate(MethodEntry in) { 75 default Object getTranslatedValue(Object value) {
185 // resolve the class entry 76 if (value instanceof Type) {
186 ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); 77 return this.getTranslatedType((Type) value);
187 if (resolvedClassEntry != null) { 78 } else if (value instanceof Handle) {
188 79 return getTranslatedHandle((Handle) value);
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 } 80 }
203 return null; 81 return value;
204 } 82 }
205 83
206 public MethodEntry translateEntry(MethodEntry in) { 84 @SuppressWarnings("unchecked")
207 String name = translate(in); 85 default <T extends Entry> T getTranslatedEntry(T entry) {
208 if (name == null) { 86 if (entry instanceof ClassDefEntry) {
209 name = in.getName(); 87 return (T) getTranslatedClassDef((ClassDefEntry) entry);
210 } 88 } else if (entry instanceof ClassEntry) {
211 return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); 89 return (T) getTranslatedClass((ClassEntry) entry);
212 } 90 } else if (entry instanceof FieldDefEntry) {
213 91 return (T) getTranslatedFieldDef((FieldDefEntry) entry);
214 public ConstructorEntry translateEntry(ConstructorEntry in) { 92 } else if (entry instanceof MethodDefEntry) {
215 if (in.isStatic()) { 93 return (T) getTranslatedMethodDef((MethodDefEntry) entry);
216 return new ConstructorEntry(translateEntry(in.getClassEntry())); 94 } else if (entry instanceof FieldEntry) {
217 } else { 95 return (T) getTranslatedField((FieldEntry) entry);
218 return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); 96 } else if (entry instanceof MethodEntry) {
219 } 97 return (T) getTranslatedMethod((MethodEntry) entry);
220 } 98 } else if (entry instanceof LocalVariableDefEntry) {
221 99 return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry);
222 public BehaviorEntry translateEntry(BehaviorEntry in) { 100 } else if (entry instanceof LocalVariableEntry) {
223 if (in instanceof MethodEntry) { 101 return (T) getTranslatedVariable((LocalVariableEntry) entry);
224 return translateEntry((MethodEntry) in); 102 } else if (entry instanceof TypeDescriptor) {
225 } else if (in instanceof ConstructorEntry) { 103 return (T) getTranslatedTypeDesc((TypeDescriptor) entry);
226 return translateEntry((ConstructorEntry) in); 104 } else if (entry instanceof MethodDescriptor) {
227 } 105 return (T) getTranslatedMethodDesc((MethodDescriptor) entry);
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 } 106 }
340 return Mappings.EntryModifier.UNCHANGED; 107 throw new IllegalArgumentException("Cannot translate unknown entry type");
341 } 108 }
342} 109}
diff --git a/src/main/java/cuchaz/enigma/mapping/Type.java b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
index 609bd64..b7b1255 100644
--- a/src/main/java/cuchaz/enigma/mapping/Type.java
+++ b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java
@@ -11,47 +11,36 @@
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;
16import cuchaz.enigma.mapping.entry.ClassEntry;
15 17
16import java.util.Map; 18import java.util.Map;
19import java.util.function.Function;
17 20
18public class Type { 21public class TypeDescriptor {
19 22
20 protected String name; 23 protected final String desc;
21 24
22 public Type(String name) { 25 public TypeDescriptor(String desc) {
26 Preconditions.checkNotNull(desc, "Desc cannot be null");
23 27
24 // don't deal with generics 28 // don't deal with generics
25 // this is just for raw jvm types 29 // this is just for raw jvm types
26 if (name.charAt(0) == 'T' || name.indexOf('<') >= 0 || name.indexOf('>') >= 0) { 30 if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) {
27 throw new IllegalArgumentException("don't use with generic types or templates: " + name); 31 throw new IllegalArgumentException("don't use with generic types or templates: " + desc);
28 } 32 }
29 33
30 this.name = name; 34 this.desc = desc;
31 }
32
33 public Type(Type other, ClassNameReplacer replacer) {
34 this.name = other.name;
35 if (other.isClass()) {
36 String replacedName = replacer.replace(other.getClassEntry().getClassName());
37 if (replacedName != null) {
38 this.name = "L" + replacedName + ";";
39 }
40 } else if (other.isArray() && other.hasClass()) {
41 String replacedName = replacer.replace(other.getClassEntry().getClassName());
42 if (replacedName != null) {
43 this.name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";";
44 }
45 }
46 } 35 }
47 36
48 public static String parseFirst(String in) { 37 public static String parseFirst(String in) {
49 38
50 if (in == null || in.length() <= 0) { 39 if (in == null || in.length() <= 0) {
51 throw new IllegalArgumentException("No type to parse, input is empty!"); 40 throw new IllegalArgumentException("No desc to parse, input is empty!");
52 } 41 }
53 42
54 // read one type from the input 43 // read one desc from the input
55 44
56 char c = in.charAt(0); 45 char c = in.charAt(0);
57 46
@@ -79,21 +68,13 @@ public class Type {
79 // then check for arrays 68 // then check for arrays
80 int dim = countArrayDimension(in); 69 int dim = countArrayDimension(in);
81 if (dim > 0) { 70 if (dim > 0) {
82 String arrayType = Type.parseFirst(in.substring(dim)); 71 String arrayType = TypeDescriptor.parseFirst(in.substring(dim));
83 return in.substring(0, dim + arrayType.length()); 72 return in.substring(0, dim + arrayType.length());
84 } 73 }
85 74
86 throw new IllegalArgumentException("don't know how to parse: " + in); 75 throw new IllegalArgumentException("don't know how to parse: " + in);
87 } 76 }
88 77
89 private static String getArrayPrefix(int dimension) {
90 StringBuilder buf = new StringBuilder();
91 for (int i = 0; i < dimension; i++) {
92 buf.append("[");
93 }
94 return buf.toString();
95 }
96
97 private static int countArrayDimension(String in) { 78 private static int countArrayDimension(String in) {
98 int i = 0; 79 int i = 0;
99 while (i < in.length() && in.charAt(i) == '[') 80 while (i < in.length() && in.charAt(i) == '[')
@@ -121,33 +102,37 @@ public class Type {
121 return null; 102 return null;
122 } 103 }
123 104
105 public static TypeDescriptor of(String name) {
106 return new TypeDescriptor("L" + name + ";");
107 }
108
124 @Override 109 @Override
125 public String toString() { 110 public String toString() {
126 return this.name; 111 return this.desc;
127 } 112 }
128 113
129 public boolean isVoid() { 114 public boolean isVoid() {
130 return this.name.length() == 1 && this.name.charAt(0) == 'V'; 115 return this.desc.length() == 1 && this.desc.charAt(0) == 'V';
131 } 116 }
132 117
133 public boolean isPrimitive() { 118 public boolean isPrimitive() {
134 return this.name.length() == 1 && Primitive.get(this.name.charAt(0)) != null; 119 return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null;
135 } 120 }
136 121
137 public Primitive getPrimitive() { 122 public Primitive getPrimitive() {
138 if (!isPrimitive()) { 123 if (!isPrimitive()) {
139 throw new IllegalStateException("not a primitive"); 124 throw new IllegalStateException("not a primitive");
140 } 125 }
141 return Primitive.get(this.name.charAt(0)); 126 return Primitive.get(this.desc.charAt(0));
142 } 127 }
143 128
144 public boolean isClass() { 129 public boolean isType() {
145 return this.name.charAt(0) == 'L' && this.name.charAt(this.name.length() - 1) == ';'; 130 return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';';
146 } 131 }
147 132
148 public ClassEntry getClassEntry() { 133 public ClassEntry getTypeEntry() {
149 if (isClass()) { 134 if (isType()) {
150 String name = this.name.substring(1, this.name.length() - 1); 135 String name = this.desc.substring(1, this.desc.length() - 1);
151 136
152 int pos = name.indexOf('<'); 137 int pos = name.indexOf('<');
153 if (pos >= 0) { 138 if (pos >= 0) {
@@ -157,46 +142,70 @@ public class Type {
157 142
158 return new ClassEntry(name); 143 return new ClassEntry(name);
159 144
160 } else if (isArray() && getArrayType().isClass()) { 145 } else if (isArray() && getArrayType().isType()) {
161 return getArrayType().getClassEntry(); 146 return getArrayType().getTypeEntry();
162 } else { 147 } else {
163 throw new IllegalStateException("type doesn't have a class"); 148 throw new IllegalStateException("desc doesn't have a class");
164 } 149 }
165 } 150 }
166 151
167 public boolean isArray() { 152 public boolean isArray() {
168 return this.name.charAt(0) == '['; 153 return this.desc.charAt(0) == '[';
169 } 154 }
170 155
171 public int getArrayDimension() { 156 public int getArrayDimension() {
172 if (!isArray()) { 157 if (!isArray()) {
173 throw new IllegalStateException("not an array"); 158 throw new IllegalStateException("not an array");
174 } 159 }
175 return countArrayDimension(this.name); 160 return countArrayDimension(this.desc);
176 } 161 }
177 162
178 public Type getArrayType() { 163 public TypeDescriptor getArrayType() {
179 if (!isArray()) { 164 if (!isArray()) {
180 throw new IllegalStateException("not an array"); 165 throw new IllegalStateException("not an array");
181 } 166 }
182 return new Type(this.name.substring(getArrayDimension(), this.name.length())); 167 return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length()));
183 } 168 }
184 169
185 public boolean hasClass() { 170 public boolean containsType() {
186 return isClass() || (isArray() && getArrayType().hasClass()); 171 return isType() || (isArray() && getArrayType().containsType());
187 } 172 }
188 173
189 @Override 174 @Override
190 public boolean equals(Object other) { 175 public boolean equals(Object other) {
191 return other instanceof Type && equals((Type) other); 176 return other instanceof TypeDescriptor && equals((TypeDescriptor) other);
192 } 177 }
193 178
194 public boolean equals(Type other) { 179 public boolean equals(TypeDescriptor other) {
195 return this.name.equals(other.name); 180 return this.desc.equals(other.desc);
196 } 181 }
197 182
183 @Override
198 public int hashCode() { 184 public int hashCode() {
199 return this.name.hashCode(); 185 return this.desc.hashCode();
186 }
187
188 public TypeDescriptor remap(Function<String, String> remapper) {
189 String desc = this.desc;
190 if (isType() || (isArray() && containsType())) {
191 String replacedName = remapper.apply(this.getTypeEntry().getName());
192 if (replacedName != null) {
193 if (this.isType()) {
194 desc = "L" + replacedName + ";";
195 } else {
196 desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";";
197 }
198 }
199 }
200 return new TypeDescriptor(desc);
201 }
202
203 private static String getArrayPrefix(int dimension) {
204 StringBuilder buf = new StringBuilder();
205 for (int i = 0; i < dimension; i++) {
206 buf.append("[");
207 }
208 return buf.toString();
200 } 209 }
201 210
202 public enum Primitive { 211 public enum Primitive {
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
new file mode 100644
index 0000000..ac1fe2a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ClassDefEntry.java
@@ -0,0 +1,37 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17
18public class ClassDefEntry extends ClassEntry {
19 private final AccessFlags access;
20 private final Signature signature;
21
22 public ClassDefEntry(String className, Signature signature, AccessFlags access) {
23 super(className);
24 Preconditions.checkNotNull(signature, "Class signature cannot be null");
25 Preconditions.checkNotNull(access, "Class access cannot be null");
26 this.signature = signature;
27 this.access = access;
28 }
29
30 public Signature getSignature() {
31 return signature;
32 }
33
34 public AccessFlags getAccess() {
35 return access;
36 }
37}
diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
index 788811f..c795825 100644
--- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ClassEntry.java
@@ -9,20 +9,20 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping.entry;
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
@@ -77,6 +77,10 @@ public class ClassEntry implements Entry {
77 return this.name; 77 return this.name;
78 } 78 }
79 79
80 public boolean isArray() {
81 return this.name.lastIndexOf('[') >= 0;
82 }
83
80 public boolean isInnerClass() { 84 public boolean isInnerClass() {
81 return this.name.lastIndexOf('$') >= 0; 85 return this.name.lastIndexOf('$') >= 0;
82 } 86 }
@@ -132,11 +136,7 @@ public class ClassEntry implements Entry {
132 } 136 }
133 137
134 public String getPackageName() { 138 public String getPackageName() {
135 int pos = this.name.lastIndexOf('/'); 139 return getPackageName(this.name);
136 if (pos > 0) {
137 return this.name.substring(0, pos);
138 }
139 return null;
140 } 140 }
141 141
142 public String getSimpleName() { 142 public String getSimpleName() {
@@ -147,6 +147,14 @@ public class ClassEntry implements Entry {
147 return this.name; 147 return this.name;
148 } 148 }
149 149
150 public static String getPackageName(String name) {
151 int pos = name.lastIndexOf('/');
152 if (pos > 0) {
153 return name.substring(0, pos);
154 }
155 return null;
156 }
157
150 public ClassEntry buildClassEntry(List<ClassEntry> classChain) { 158 public ClassEntry buildClassEntry(List<ClassEntry> classChain) {
151 assert (classChain.contains(this)); 159 assert (classChain.contains(this));
152 StringBuilder buf = new StringBuilder(); 160 StringBuilder buf = new StringBuilder();
diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
index c79510b..b612140 100644
--- a/src/main/java/cuchaz/enigma/mapping/Entry.java
+++ b/src/main/java/cuchaz/enigma/mapping/entry/Entry.java
@@ -9,14 +9,14 @@
9 * Jeff Martin - initial API and implementation 9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/ 10 ******************************************************************************/
11 11
12package cuchaz.enigma.mapping; 12package cuchaz.enigma.mapping.entry;
13 13
14public interface Entry { 14public interface Entry {
15 String getName(); 15 String getName();
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/entry/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
new file mode 100644
index 0000000..5bd159f
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/EntryFactory.java
@@ -0,0 +1,49 @@
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.entry;
13
14import cuchaz.enigma.analysis.JarIndex;
15import cuchaz.enigma.mapping.ClassMapping;
16import cuchaz.enigma.mapping.FieldMapping;
17import cuchaz.enigma.mapping.MethodDescriptor;
18import cuchaz.enigma.mapping.MethodMapping;
19
20public class EntryFactory {
21 public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) {
22 ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName());
23 return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry));
24 }
25
26 private static ClassEntry getObfClassEntry(ClassMapping classMapping) {
27 return new ClassEntry(classMapping.getObfFullName());
28 }
29
30 public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) {
31 return new ClassEntry(classMapping.getDeobfName());
32 }
33
34 public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) {
35 return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc());
36 }
37
38 public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) {
39 return new MethodEntry(classEntry, name, desc);
40 }
41
42 public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) {
43 return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc());
44 }
45
46 public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) {
47 return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping);
48 }
49}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
new file mode 100644
index 0000000..d18115b
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/FieldDefEntry.java
@@ -0,0 +1,43 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.Signature;
17import cuchaz.enigma.mapping.TypeDescriptor;
18
19public class FieldDefEntry extends FieldEntry {
20 private final AccessFlags access;
21 private final Signature signature;
22
23 public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, Signature signature, AccessFlags access) {
24 super(ownerEntry, name, desc);
25 Preconditions.checkNotNull(access, "Field access cannot be null");
26 Preconditions.checkNotNull(signature, "Field signature cannot be null");
27 this.access = access;
28 this.signature = signature;
29 }
30
31 public AccessFlags getAccess() {
32 return access;
33 }
34
35 public Signature getSignature() {
36 return signature;
37 }
38
39 @Override
40 public FieldDefEntry updateOwnership(ClassEntry owner) {
41 return new FieldDefEntry(owner, this.name, this.desc, signature, access);
42 }
43}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
new file mode 100644
index 0000000..b6e1554
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/FieldEntry.java
@@ -0,0 +1,77 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.TypeDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class FieldEntry implements Entry {
19
20 protected final ClassEntry ownerEntry;
21 protected final String name;
22 protected final TypeDescriptor desc;
23
24 // NOTE: this argument order is important for the MethodReader/MethodWriter
25 public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
26 Preconditions.checkNotNull(ownerEntry, "Owner cannot be null");
27 Preconditions.checkNotNull(name, "Field name cannot be null");
28 Preconditions.checkNotNull(desc, "Field descriptor cannot be null");
29
30 this.ownerEntry = ownerEntry;
31 this.name = name;
32 this.desc = desc;
33 }
34
35 @Override
36 public ClassEntry getOwnerClassEntry() {
37 return this.ownerEntry;
38 }
39
40 @Override
41 public String getName() {
42 return this.name;
43 }
44
45 @Override
46 public String getClassName() {
47 return this.ownerEntry.getName();
48 }
49
50 public TypeDescriptor getDesc() {
51 return this.desc;
52 }
53
54 @Override
55 public FieldEntry updateOwnership(ClassEntry owner) {
56 return new FieldEntry(owner, this.name, this.desc);
57 }
58
59 @Override
60 public int hashCode() {
61 return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc);
62 }
63
64 @Override
65 public boolean equals(Object other) {
66 return other instanceof FieldEntry && equals((FieldEntry) other);
67 }
68
69 public boolean equals(FieldEntry other) {
70 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc);
71 }
72
73 @Override
74 public String toString() {
75 return this.ownerEntry.getName() + "." + this.name + ":" + this.desc;
76 }
77}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
new file mode 100644
index 0000000..7742272
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableDefEntry.java
@@ -0,0 +1,57 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.TypeDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableDefEntry extends LocalVariableEntry {
13
14 protected final MethodDefEntry ownerEntry;
15 protected final TypeDescriptor desc;
16
17 public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) {
18 super(ownerEntry, index, name);
19 Preconditions.checkNotNull(desc, "Variable desc cannot be null");
20
21 this.ownerEntry = ownerEntry;
22 this.desc = desc;
23 }
24
25 @Override
26 public MethodDefEntry getOwnerEntry() {
27 return this.ownerEntry;
28 }
29
30 public TypeDescriptor getDesc() {
31 return desc;
32 }
33
34 @Override
35 public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) {
36 return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, desc);
37 }
38
39 @Override
40 public int hashCode() {
41 return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index));
42 }
43
44 @Override
45 public boolean equals(Object other) {
46 return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other);
47 }
48
49 public boolean equals(LocalVariableDefEntry other) {
50 return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index;
51 }
52
53 @Override
54 public String toString() {
55 return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")";
56 }
57}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
new file mode 100644
index 0000000..a794d0a
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/LocalVariableEntry.java
@@ -0,0 +1,82 @@
1package cuchaz.enigma.mapping.entry;
2
3import com.google.common.base.Preconditions;
4import cuchaz.enigma.mapping.MethodDescriptor;
5import cuchaz.enigma.utils.Utils;
6
7/**
8 * TypeDescriptor...
9 * Created by Thog
10 * 19/10/2016
11 */
12public class LocalVariableEntry implements Entry {
13
14 protected final MethodEntry ownerEntry;
15 protected final String name;
16 protected final int index;
17
18 public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) {
19 Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null");
20 Preconditions.checkNotNull(name, "Variable name cannot be null");
21 Preconditions.checkArgument(index >= 0, "Index must be positive");
22
23 this.ownerEntry = ownerEntry;
24 this.name = name;
25 this.index = index;
26 }
27
28 public MethodEntry getOwnerEntry() {
29 return this.ownerEntry;
30 }
31
32 public int getIndex() {
33 return index;
34 }
35
36 @Override
37 public String getName() {
38 return this.name;
39 }
40
41 @Override
42 public ClassEntry getOwnerClassEntry() {
43 return this.ownerEntry.getOwnerClassEntry();
44 }
45
46 @Override
47 public String getClassName() {
48 return this.ownerEntry.getClassName();
49 }
50
51 @Override
52 public LocalVariableEntry updateOwnership(ClassEntry classEntry) {
53 return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name);
54 }
55
56 public String getMethodName() {
57 return this.ownerEntry.getName();
58 }
59
60 public MethodDescriptor getMethodDesc() {
61 return this.ownerEntry.getDesc();
62 }
63
64 @Override
65 public int hashCode() {
66 return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index));
67 }
68
69 @Override
70 public boolean equals(Object other) {
71 return other instanceof LocalVariableEntry && equals((LocalVariableEntry) other);
72 }
73
74 public boolean equals(LocalVariableEntry other) {
75 return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index;
76 }
77
78 @Override
79 public String toString() {
80 return this.ownerEntry + "(" + this.index + ":" + this.name + ")";
81 }
82}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java
new file mode 100644
index 0000000..bb7c85e
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/MethodDefEntry.java
@@ -0,0 +1,54 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.bytecode.AccessFlags;
16import cuchaz.enigma.mapping.MethodDescriptor;
17import cuchaz.enigma.mapping.Signature;
18
19public class MethodDefEntry extends MethodEntry {
20
21 private final AccessFlags access;
22 private final Signature signature;
23
24 public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, Signature signature, AccessFlags access) {
25 super(classEntry, name, descriptor);
26 Preconditions.checkNotNull(access, "Method access cannot be null");
27 Preconditions.checkNotNull(signature, "Method signature cannot be null");
28 this.access = access;
29 this.signature = signature;
30 }
31
32 public AccessFlags getAccess() {
33 return access;
34 }
35
36 public Signature getSignature() {
37 return signature;
38 }
39
40 public int getVariableOffset(ClassDefEntry ownerEntry) {
41 // Enum constructors have an implicit "name" and "ordinal" parameter as well as "this"
42 if (ownerEntry.getAccess().isEnum() && getName().startsWith("<")) {
43 return 3;
44 } else {
45 // If we're not static, "this" is bound to index 0
46 return getAccess().isStatic() ? 0 : 1;
47 }
48 }
49
50 @Override
51 public MethodDefEntry updateOwnership(ClassEntry classEntry) {
52 return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, signature, access);
53 }
54}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
new file mode 100644
index 0000000..1abc5b1
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/MethodEntry.java
@@ -0,0 +1,80 @@
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.entry;
13
14import com.google.common.base.Preconditions;
15import cuchaz.enigma.mapping.MethodDescriptor;
16import cuchaz.enigma.utils.Utils;
17
18public class MethodEntry implements Entry {
19
20 protected final ClassEntry classEntry;
21 protected final String name;
22 protected final MethodDescriptor descriptor;
23
24 public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) {
25 Preconditions.checkNotNull(classEntry, "Class cannot be null");
26 Preconditions.checkNotNull(name, "Method name cannot be null");
27 Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null");
28
29 this.classEntry = classEntry;
30 this.name = name;
31 this.descriptor = descriptor;
32 }
33
34 @Override
35 public ClassEntry getOwnerClassEntry() {
36 return this.classEntry;
37 }
38
39 @Override
40 public String getName() {
41 return this.name;
42 }
43
44 public MethodDescriptor getDesc() {
45 return this.descriptor;
46 }
47
48 public boolean isConstructor() {
49 return name.equals("<init>") || name.equals("<clinit>");
50 }
51
52 @Override
53 public String getClassName() {
54 return this.classEntry.getName();
55 }
56
57 @Override
58 public MethodEntry updateOwnership(ClassEntry classEntry) {
59 return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor);
60 }
61
62 @Override
63 public int hashCode() {
64 return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor);
65 }
66
67 @Override
68 public boolean equals(Object other) {
69 return other instanceof MethodEntry && equals((MethodEntry) other);
70 }
71
72 public boolean equals(MethodEntry other) {
73 return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc());
74 }
75
76 @Override
77 public String toString() {
78 return this.classEntry.getName() + "." + this.name + this.descriptor;
79 }
80}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java
new file mode 100644
index 0000000..73770c5
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ProcyonEntryFactory.java
@@ -0,0 +1,48 @@
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.entry;
13
14import com.strobel.assembler.metadata.FieldDefinition;
15import com.strobel.assembler.metadata.MemberReference;
16import com.strobel.assembler.metadata.MethodDefinition;
17import cuchaz.enigma.bytecode.AccessFlags;
18import cuchaz.enigma.mapping.MethodDescriptor;
19import cuchaz.enigma.mapping.Signature;
20import cuchaz.enigma.mapping.TypeDescriptor;
21
22public class ProcyonEntryFactory {
23 private final ReferencedEntryPool entryPool;
24
25 public ProcyonEntryFactory(ReferencedEntryPool entryPool) {
26 this.entryPool = entryPool;
27 }
28
29 public FieldEntry getFieldEntry(MemberReference def) {
30 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
31 return entryPool.getField(classEntry, def.getName(), def.getErasedSignature());
32 }
33
34 public FieldDefEntry getFieldDefEntry(FieldDefinition def) {
35 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
36 return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), Signature.createTypedSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
37 }
38
39 public MethodEntry getMethodEntry(MemberReference def) {
40 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
41 return entryPool.getMethod(classEntry, def.getName(), def.getErasedSignature());
42 }
43
44 public MethodDefEntry getMethodDefEntry(MethodDefinition def) {
45 ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName());
46 return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), Signature.createSignature(def.getSignature()), new AccessFlags(def.getModifiers()));
47 }
48}
diff --git a/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java
new file mode 100644
index 0000000..338d209
--- /dev/null
+++ b/src/main/java/cuchaz/enigma/mapping/entry/ReferencedEntryPool.java
@@ -0,0 +1,53 @@
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.entry;
13
14import cuchaz.enigma.mapping.MethodDescriptor;
15import cuchaz.enigma.mapping.TypeDescriptor;
16
17import java.util.HashMap;
18import java.util.Map;
19
20public class ReferencedEntryPool {
21 private final Map<String, ClassEntry> classEntries = new HashMap<>();
22 private final Map<String, Map<String, MethodEntry>> methodEntries = new HashMap<>();
23 private final Map<String, Map<String, FieldEntry>> fieldEntries = new HashMap<>();
24
25 public ClassEntry getClass(String name) {
26 return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(name));
27 }
28
29 public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) {
30 return getMethod(ownerEntry, name, new MethodDescriptor(desc));
31 }
32
33 public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) {
34 String key = name + desc.toString();
35 return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc));
36 }
37
38 public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) {
39 return getField(ownerEntry, name, new TypeDescriptor(desc));
40 }
41
42 public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) {
43 return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc));
44 }
45
46 private Map<String, MethodEntry> getClassMethods(String name) {
47 return methodEntries.computeIfAbsent(name, s -> new HashMap<>());
48 }
49
50 private Map<String, FieldEntry> getClassFields(String name) {
51 return fieldEntries.computeIfAbsent(name, s -> new HashMap<>());
52 }
53}