From 2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sat, 19 May 2018 17:02:46 +0200 Subject: Initial port to ASM --- .../java/cuchaz/enigma/mapping/ArgumentEntry.java | 110 ------- .../cuchaz/enigma/mapping/ArgumentMapping.java | 50 --- .../java/cuchaz/enigma/mapping/BehaviorEntry.java | 16 - .../java/cuchaz/enigma/mapping/ClassDefEntry.java | 29 ++ .../java/cuchaz/enigma/mapping/ClassEntry.java | 26 +- .../java/cuchaz/enigma/mapping/ClassMapping.java | 163 +++++----- .../cuchaz/enigma/mapping/ClassNameReplacer.java | 16 - .../cuchaz/enigma/mapping/ConstructorEntry.java | 105 ------- .../enigma/mapping/DirectionalTranslator.java | 319 +++++++++++++++++++ src/main/java/cuchaz/enigma/mapping/Entry.java | 4 +- .../java/cuchaz/enigma/mapping/EntryFactory.java | 101 +----- .../java/cuchaz/enigma/mapping/FieldDefEntry.java | 34 ++ .../java/cuchaz/enigma/mapping/FieldEntry.java | 51 ++- .../java/cuchaz/enigma/mapping/FieldMapping.java | 36 +-- .../enigma/mapping/LocalVariableDefEntry.java | 75 +++++ .../cuchaz/enigma/mapping/LocalVariableEntry.java | 65 ++-- .../enigma/mapping/LocalVariableMapping.java | 50 +++ src/main/java/cuchaz/enigma/mapping/Mappings.java | 45 ++- .../cuchaz/enigma/mapping/MappingsChecker.java | 10 +- .../enigma/mapping/MappingsEnigmaReader.java | 18 +- .../enigma/mapping/MappingsEnigmaWriter.java | 16 +- .../cuchaz/enigma/mapping/MappingsRenamer.java | 163 +++++----- .../cuchaz/enigma/mapping/MappingsSRGWriter.java | 6 +- .../cuchaz/enigma/mapping/MappingsTinyReader.java | 4 +- .../java/cuchaz/enigma/mapping/MethodDefEntry.java | 35 +++ .../cuchaz/enigma/mapping/MethodDescriptor.java | 113 +++++++ .../java/cuchaz/enigma/mapping/MethodEntry.java | 59 ++-- .../java/cuchaz/enigma/mapping/MethodMapping.java | 130 ++++---- .../java/cuchaz/enigma/mapping/NameValidator.java | 15 +- .../cuchaz/enigma/mapping/ProcyonEntryFactory.java | 36 ++- .../cuchaz/enigma/mapping/ReferencedEntryPool.java | 50 +++ src/main/java/cuchaz/enigma/mapping/Signature.java | 106 ------- .../enigma/mapping/TranslationDirection.java | 10 +- .../java/cuchaz/enigma/mapping/Translator.java | 344 ++------------------- .../java/cuchaz/enigma/mapping/TypeDescriptor.java | 240 ++++++++++++++ 35 files changed, 1410 insertions(+), 1240 deletions(-) delete mode 100644 src/main/java/cuchaz/enigma/mapping/ArgumentEntry.java delete mode 100644 src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java delete mode 100644 src/main/java/cuchaz/enigma/mapping/BehaviorEntry.java create mode 100644 src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java delete mode 100644 src/main/java/cuchaz/enigma/mapping/ClassNameReplacer.java delete mode 100644 src/main/java/cuchaz/enigma/mapping/ConstructorEntry.java create mode 100644 src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java create mode 100644 src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java create mode 100644 src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java create mode 100644 src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java create mode 100644 src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java create mode 100644 src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java create mode 100644 src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java delete mode 100644 src/main/java/cuchaz/enigma/mapping/Signature.java create mode 100644 src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java (limited to 'src/main/java/cuchaz/enigma/mapping') 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 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.mapping; - -import cuchaz.enigma.utils.Utils; - -public class ArgumentEntry implements Entry { - - private BehaviorEntry behaviorEntry; - private int index; - private String name; - - public ArgumentEntry(BehaviorEntry behaviorEntry, int index, String name) { - if (behaviorEntry == null) { - throw new IllegalArgumentException("Behavior cannot be null!"); - } - if (index < 0) { - throw new IllegalArgumentException("Index must be non-negative!"); - } - if (name == null) { - throw new IllegalArgumentException("Argument name cannot be null!"); - } - - this.behaviorEntry = behaviorEntry; - this.index = index; - this.name = name; - } - - public ArgumentEntry(ArgumentEntry other) { - this.behaviorEntry = other.getBehaviorEntry(); - this.index = other.index; - this.name = other.name; - } - - public ArgumentEntry(ArgumentEntry other, String newClassName) { - this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(new ClassEntry(newClassName)); - this.index = other.index; - this.name = other.name; - } - - public ArgumentEntry(ArgumentEntry other, BehaviorEntry entry) { - this.behaviorEntry = entry; - this.index = other.index; - this.name = other.name; - } - - public BehaviorEntry getBehaviorEntry() { - return this.behaviorEntry; - } - - public int getIndex() { - return this.index; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public ClassEntry getClassEntry() { - return this.behaviorEntry.getClassEntry(); - } - - @Override - public String getClassName() { - return this.behaviorEntry.getClassName(); - } - - @Override - public ArgumentEntry cloneToNewClass(ClassEntry classEntry) { - return new ArgumentEntry(this, classEntry.getName()); - } - - public String getMethodName() { - return this.behaviorEntry.getName(); - } - - public Signature getMethodSignature() { - return this.behaviorEntry.getSignature(); - } - - @Override - public int hashCode() { - return Utils.combineHashesOrdered(this.behaviorEntry, Integer.valueOf(this.index).hashCode(), this.name.hashCode()); - } - - @Override - public boolean equals(Object other) { - return other instanceof ArgumentEntry && equals((ArgumentEntry) other); - } - - public boolean equals(ArgumentEntry other) { - return this.behaviorEntry.equals(other.behaviorEntry) && this.index == other.index && this.name.equals(other.name); - } - - @Override - public String toString() { - return this.behaviorEntry + "(" + this.index + ":" + this.name + ")"; - } -} diff --git a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java b/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java deleted file mode 100644 index 91ecd10..0000000 --- a/src/main/java/cuchaz/enigma/mapping/ArgumentMapping.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.mapping; - -public class ArgumentMapping implements Comparable { - - private int index; - private String name; - - // NOTE: this argument order is important for the MethodReader/MethodWriter - public ArgumentMapping(int index, String name) { - this.index = index; - this.name = NameValidator.validateArgumentName(name); - } - - public ArgumentMapping(ArgumentMapping other) { - this.index = other.index; - this.name = other.name; - } - - public int getIndex() { - return this.index; - } - - public String getName() { - return this.name; - } - - public void setName(String val) { - this.name = NameValidator.validateArgumentName(val); - } - - public ArgumentEntry getObfEntry(BehaviorEntry behaviorEntry) { - return new ArgumentEntry(behaviorEntry, index, name); - } - - @Override - public int compareTo(ArgumentMapping other) { - return Integer.compare(this.index, other.index); - } -} 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 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.mapping; - -public interface BehaviorEntry extends Entry { - Signature getSignature(); -} diff --git a/src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java new file mode 100644 index 0000000..dc1b02e --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/ClassDefEntry.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +import com.google.common.base.Preconditions; +import cuchaz.enigma.bytecode.AccessFlags; + +public class ClassDefEntry extends ClassEntry { + private final AccessFlags access; + + public ClassDefEntry(String className, AccessFlags access) { + super(className); + Preconditions.checkNotNull(access, "Class access cannot be null"); + this.access = access; + } + + public AccessFlags getAccess() { + return access; + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java index 788811f..a49f8dd 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassEntry.java @@ -11,18 +11,18 @@ package cuchaz.enigma.mapping; +import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import java.util.List; public class ClassEntry implements Entry { - private String name; + private final String name; public ClassEntry(String className) { - if (className == null) { - throw new IllegalArgumentException("Class name cannot be null!"); - } + Preconditions.checkNotNull(className, "Class name cannot be null"); + if (className.indexOf('.') >= 0) { throw new IllegalArgumentException("Class name must be in JVM format. ie, path/to/package/class$inner : " + className); } @@ -49,12 +49,12 @@ public class ClassEntry implements Entry { } @Override - public ClassEntry getClassEntry() { + public ClassEntry getOwnerClassEntry() { return this; } @Override - public ClassEntry cloneToNewClass(ClassEntry classEntry) { + public ClassEntry updateOwnership(ClassEntry classEntry) { return classEntry; } @@ -132,11 +132,7 @@ public class ClassEntry implements Entry { } public String getPackageName() { - int pos = this.name.lastIndexOf('/'); - if (pos > 0) { - return this.name.substring(0, pos); - } - return null; + return getPackageName(this.name); } public String getSimpleName() { @@ -147,6 +143,14 @@ public class ClassEntry implements Entry { return this.name; } + public static String getPackageName(String name) { + int pos = name.lastIndexOf('/'); + if (pos > 0) { + return name.substring(0, pos); + } + return null; + } + public ClassEntry buildClassEntry(List classChain) { assert (classChain.contains(this)); StringBuilder buf = new StringBuilder(); diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java index 51751ca..c782250 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java @@ -34,7 +34,6 @@ public class ClassMapping implements Comparable { private Map methodsByDeobf; private boolean isDirty; private Mappings.EntryModifier modifier; - private boolean deobfInner; public ClassMapping(String obfFullName) { this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); @@ -81,6 +80,10 @@ public class ClassMapping implements Comparable { return deobfName; } + public String getTranslatedName(TranslationDirection direction) { + return direction.choose(deobfName, obfFullName); + } + //// INNER CLASSES //////// public void setDeobfName(String val) { @@ -191,21 +194,21 @@ public class ClassMapping implements Comparable { return fieldsByObf.values(); } - public boolean containsObfField(String obfName, Type obfType) { - return fieldsByObf.containsKey(getFieldKey(obfName, obfType)); + public boolean containsObfField(String obfName, TypeDescriptor obfDesc) { + return fieldsByObf.containsKey(getFieldKey(obfName, obfDesc)); } - public boolean containsDeobfField(String deobfName, Type deobfType) { - return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfType)); + public boolean containsDeobfField(String deobfName, TypeDescriptor deobfDesc) { + return fieldsByDeobf.containsKey(getFieldKey(deobfName, deobfDesc)); } public void addFieldMapping(FieldMapping fieldMapping) { - String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); + String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()); if (fieldsByObf.containsKey(obfKey)) { throw new Error("Already have mapping for " + obfFullName + "." + obfKey); } if (fieldMapping.getDeobfName() != null) { - String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); + String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc()); if (fieldsByDeobf.containsKey(deobfKey)) { throw new Error("Already have mapping for " + deobfName + "." + deobfKey); } @@ -218,63 +221,67 @@ public class ClassMapping implements Comparable { } public void removeFieldMapping(FieldMapping fieldMapping) { - boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType())) != null; + boolean obfWasRemoved = fieldsByObf.remove(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc())) != null; assert (obfWasRemoved); if (fieldMapping.getDeobfName() != null) { - boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType())) != null; + boolean deobfWasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) != null; assert (deobfWasRemoved); } this.isDirty = true; } - public FieldMapping getFieldByObf(String obfName, Type obfType) { - return fieldsByObf.get(getFieldKey(obfName, obfType)); + public FieldMapping getFieldByObf(String obfName, TypeDescriptor obfDesc) { + return fieldsByObf.get(getFieldKey(obfName, obfDesc)); + } + + public FieldMapping getFieldByObf(FieldEntry field) { + return getFieldByObf(field.getName(), field.getDesc()); } - public FieldMapping getFieldByDeobf(String deobfName, Type obfType) { - return fieldsByDeobf.get(getFieldKey(deobfName, obfType)); + public FieldMapping getFieldByDeobf(String deobfName, TypeDescriptor obfDesc) { + return fieldsByDeobf.get(getFieldKey(deobfName, obfDesc)); } - public String getObfFieldName(String deobfName, Type obfType) { - FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfType)); + public String getObfFieldName(String deobfName, TypeDescriptor obfDesc) { + FieldMapping fieldMapping = fieldsByDeobf.get(getFieldKey(deobfName, obfDesc)); if (fieldMapping != null) { return fieldMapping.getObfName(); } return null; } - public String getDeobfFieldName(String obfName, Type obfType) { - FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); + public String getDeobfFieldName(String obfName, TypeDescriptor obfDesc) { + FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc)); if (fieldMapping != null) { return fieldMapping.getDeobfName(); } return null; } - private String getFieldKey(String name, Type type) { + private String getFieldKey(String name, TypeDescriptor desc) { if (name == null) { throw new IllegalArgumentException("name cannot be null!"); } - if (type == null) { - throw new IllegalArgumentException("type cannot be null!"); + if (desc == null) { + throw new IllegalArgumentException("desc cannot be null!"); } - return name + ":" + type; + return name + ":" + desc; } - public void setFieldName(String obfName, Type obfType, String deobfName) { + public void setFieldName(String obfName, TypeDescriptor obfDesc, String deobfName) { assert (deobfName != null); - FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfType)); + FieldMapping fieldMapping = fieldsByObf.get(getFieldKey(obfName, obfDesc)); if (fieldMapping == null) { - fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); - boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; + fieldMapping = new FieldMapping(obfName, obfDesc, deobfName, Mappings.EntryModifier.UNCHANGED); + boolean obfWasAdded = fieldsByObf.put(getFieldKey(obfName, obfDesc), fieldMapping) == null; assert (obfWasAdded); } else { - boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfType)) != null; + boolean wasRemoved = fieldsByDeobf.remove(getFieldKey(fieldMapping.getDeobfName(), obfDesc)) != null; assert (wasRemoved); } fieldMapping.setDeobfName(deobfName); if (deobfName != null) { - boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfType), fieldMapping) == null; + boolean wasAdded = fieldsByDeobf.put(getFieldKey(deobfName, obfDesc), fieldMapping) == null; assert (wasAdded); } this.isDirty = true; @@ -282,13 +289,13 @@ public class ClassMapping implements Comparable { //// METHODS //////// - public void setFieldObfNameAndType(String oldObfName, Type obfType, String newObfName, Type newObfType) { + public void setFieldObfNameAndType(String oldObfName, TypeDescriptor obfDesc, String newObfName, TypeDescriptor newObfDesc) { assert (newObfName != null); - FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfType)); + FieldMapping fieldMapping = fieldsByObf.remove(getFieldKey(oldObfName, obfDesc)); assert (fieldMapping != null); fieldMapping.setObfName(newObfName); - fieldMapping.setObfType(newObfType); - boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfType), fieldMapping) == null; + fieldMapping.setObfDesc(newObfDesc); + boolean obfWasAdded = fieldsByObf.put(getFieldKey(newObfName, newObfDesc), fieldMapping) == null; assert (obfWasAdded); this.isDirty = true; } @@ -298,23 +305,23 @@ public class ClassMapping implements Comparable { return methodsByObf.values(); } - public boolean containsObfMethod(String obfName, Signature obfSignature) { - return methodsByObf.containsKey(getMethodKey(obfName, obfSignature)); + public boolean containsObfMethod(String obfName, MethodDescriptor obfDescriptor) { + return methodsByObf.containsKey(getMethodKey(obfName, obfDescriptor)); } - public boolean containsDeobfMethod(String deobfName, Signature obfSignature) { - return methodsByDeobf.containsKey(getMethodKey(deobfName, obfSignature)); + public boolean containsDeobfMethod(String deobfName, MethodDescriptor obfDescriptor) { + return methodsByDeobf.containsKey(getMethodKey(deobfName, obfDescriptor)); } public void addMethodMapping(MethodMapping methodMapping) { - String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()); if (methodsByObf.containsKey(obfKey)) { throw new Error("Already have mapping for " + obfFullName + "." + obfKey); } boolean wasAdded = methodsByObf.put(obfKey, methodMapping) == null; assert (wasAdded); if (methodMapping.getDeobfName() != null) { - String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature()); + String deobfKey = getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc()); if (methodsByDeobf.containsKey(deobfKey)) { throw new Error("Already have mapping for " + deobfName + "." + deobfKey); } @@ -326,44 +333,48 @@ public class ClassMapping implements Comparable { } public void removeMethodMapping(MethodMapping methodMapping) { - boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature())) != null; + boolean obfWasRemoved = methodsByObf.remove(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc())) != null; assert (obfWasRemoved); if (methodMapping.getDeobfName() != null) { - boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + boolean deobfWasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null; assert (deobfWasRemoved); } this.isDirty = true; } - public MethodMapping getMethodByObf(String obfName, Signature obfSignature) { - return methodsByObf.get(getMethodKey(obfName, obfSignature)); + public MethodMapping getMethodByObf(String obfName, MethodDescriptor obfDescriptor) { + return methodsByObf.get(getMethodKey(obfName, obfDescriptor)); + } + + public MethodMapping getMethodByObf(MethodEntry method) { + return getMethodByObf(method.getName(), method.getDesc()); } - public MethodMapping getMethodByDeobf(String deobfName, Signature obfSignature) { - return methodsByDeobf.get(getMethodKey(deobfName, obfSignature)); + public MethodMapping getMethodByDeobf(String deobfName, MethodDescriptor obfDescriptor) { + return methodsByDeobf.get(getMethodKey(deobfName, obfDescriptor)); } - private String getMethodKey(String name, Signature signature) { + private String getMethodKey(String name, MethodDescriptor descriptor) { if (name == null) { throw new IllegalArgumentException("name cannot be null!"); } - if (signature == null) { - throw new IllegalArgumentException("signature cannot be null!"); + if (descriptor == null) { + throw new IllegalArgumentException("descriptor cannot be null!"); } - return name + signature; + return name + descriptor; } - public void setMethodName(String obfName, Signature obfSignature, String deobfName) { - MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfSignature)); + public void setMethodName(String obfName, MethodDescriptor obfDescriptor, String deobfName) { + MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfName, obfDescriptor)); if (methodMapping == null) { - methodMapping = createMethodMapping(obfName, obfSignature); + methodMapping = createMethodMapping(obfName, obfDescriptor); } else if (methodMapping.getDeobfName() != null) { - boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfSignature())) != null; + boolean wasRemoved = methodsByDeobf.remove(getMethodKey(methodMapping.getDeobfName(), methodMapping.getObfDesc())) != null; assert (wasRemoved); } methodMapping.setDeobfName(deobfName); if (deobfName != null) { - boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfSignature), methodMapping) == null; + boolean wasAdded = methodsByDeobf.put(getMethodKey(deobfName, obfDescriptor), methodMapping) == null; assert (wasAdded); } this.isDirty = true; @@ -371,35 +382,35 @@ public class ClassMapping implements Comparable { //// ARGUMENTS //////// - public void setMethodObfNameAndSignature(String oldObfName, Signature obfSignature, String newObfName, Signature newObfSignature) { + public void setMethodObfNameAndSignature(String oldObfName, MethodDescriptor obfDescriptor, String newObfName, MethodDescriptor newObfDescriptor) { assert (newObfName != null); - MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfSignature)); + MethodMapping methodMapping = methodsByObf.remove(getMethodKey(oldObfName, obfDescriptor)); assert (methodMapping != null); methodMapping.setObfName(newObfName); - methodMapping.setObfSignature(newObfSignature); - boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfSignature), methodMapping) == null; + methodMapping.setObfDescriptor(newObfDescriptor); + boolean obfWasAdded = methodsByObf.put(getMethodKey(newObfName, newObfDescriptor), methodMapping) == null; assert (obfWasAdded); this.isDirty = true; } - public void setArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex, String argumentName) { + public void setArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex, String argumentName) { assert (argumentName != null); - MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)); + MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)); if (methodMapping == null) { - methodMapping = createMethodMapping(obfMethodName, obfMethodSignature); + methodMapping = createMethodMapping(obfMethodName, obfMethodDescriptor); } - methodMapping.setArgumentName(argumentIndex, argumentName); + methodMapping.setLocalVariableName(argumentIndex, argumentName); this.isDirty = true; } - public void removeArgumentName(String obfMethodName, Signature obfMethodSignature, int argumentIndex) { - methodsByObf.get(getMethodKey(obfMethodName, obfMethodSignature)).removeArgumentName(argumentIndex); + public void removeArgumentName(String obfMethodName, MethodDescriptor obfMethodDescriptor, int argumentIndex) { + methodsByObf.get(getMethodKey(obfMethodName, obfMethodDescriptor)).removeLocalVariableName(argumentIndex); this.isDirty = true; } - private MethodMapping createMethodMapping(String obfName, Signature obfSignature) { - MethodMapping methodMapping = new MethodMapping(obfName, obfSignature); - boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfSignature), methodMapping) == null; + private MethodMapping createMethodMapping(String obfName, MethodDescriptor obfDescriptor) { + MethodMapping methodMapping = new MethodMapping(obfName, obfDescriptor); + boolean wasAdded = methodsByObf.put(getMethodKey(obfName, obfDescriptor), methodMapping) == null; assert (wasAdded); this.isDirty = true; return methodMapping; @@ -459,24 +470,24 @@ public class ClassMapping implements Comparable { // rename field types for (FieldMapping fieldMapping : new ArrayList<>(fieldsByObf.values())) { - String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); + String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()); if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { boolean wasRemoved = fieldsByObf.remove(oldFieldKey) != null; assert (wasRemoved); boolean wasAdded = fieldsByObf - .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; + .put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfDesc()), fieldMapping) == null; assert (wasAdded); } } // rename method signatures for (MethodMapping methodMapping : new ArrayList<>(methodsByObf.values())) { - String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); + String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()); if (methodMapping.renameObfClass(oldObfClassName, newObfClassName)) { boolean wasRemoved = methodsByObf.remove(oldMethodKey) != null; assert (wasRemoved); boolean wasAdded = methodsByObf - .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()), methodMapping) == null; + .put(getMethodKey(methodMapping.getObfName(), methodMapping.getObfDesc()), methodMapping) == null; assert (wasAdded); } } @@ -490,9 +501,9 @@ public class ClassMapping implements Comparable { return false; } - public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { - MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfBehaviorEntry.getName(), obfBehaviorEntry.getSignature())); - return methodMapping != null && methodMapping.containsArgument(name); + public boolean containsArgument(MethodEntry obfMethodEntry, String name) { + MethodMapping methodMapping = methodsByObf.get(getMethodKey(obfMethodEntry.getName(), obfMethodEntry.getDesc())); + return methodMapping != null && methodMapping.containsLocalVariable(name); } public ClassEntry getObfEntry() { @@ -521,9 +532,9 @@ public class ClassMapping implements Comparable { this.modifier = modifier; } - public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { - FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfType), - k -> new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED)); + public void setFieldModifier(String obfName, TypeDescriptor obfDesc, Mappings.EntryModifier modifier) { + FieldMapping fieldMapping = fieldsByObf.computeIfAbsent(getFieldKey(obfName, obfDesc), + k -> new FieldMapping(obfName, obfDesc, null, Mappings.EntryModifier.UNCHANGED)); if (fieldMapping.getModifier() != modifier) { fieldMapping.setModifier(modifier); @@ -531,7 +542,7 @@ public class ClassMapping implements Comparable { } } - public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { + public void setMethodModifier(String obfName, MethodDescriptor sig, Mappings.EntryModifier modifier) { MethodMapping methodMapping = methodsByObf.computeIfAbsent(getMethodKey(obfName, sig), k -> new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED)); 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 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.mapping; - -public interface ClassNameReplacer { - String replace(String className); -} 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 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.mapping; - -import cuchaz.enigma.utils.Utils; - -public class ConstructorEntry implements BehaviorEntry { - - private ClassEntry classEntry; - private Signature signature; - - public ConstructorEntry(ClassEntry classEntry) { - this(classEntry, null); - } - - public ConstructorEntry(ClassEntry classEntry, Signature signature) { - if (classEntry == null) { - throw new IllegalArgumentException("Class cannot be null!"); - } - - this.classEntry = classEntry; - this.signature = signature; - } - - public ConstructorEntry(ConstructorEntry other, String newClassName) { - this.classEntry = new ClassEntry(newClassName); - this.signature = other.signature; - } - - @Override - public ClassEntry getClassEntry() { - return this.classEntry; - } - - @Override - public String getName() { - if (isStatic()) { - return ""; - } - return ""; - } - - public boolean isStatic() { - return this.signature == null; - } - - @Override - public Signature getSignature() { - return this.signature; - } - - @Override - public String getClassName() { - return this.classEntry.getName(); - } - - @Override - public ConstructorEntry cloneToNewClass(ClassEntry classEntry) { - return new ConstructorEntry(this, classEntry.getName()); - } - - @Override - public int hashCode() { - if (isStatic()) { - return Utils.combineHashesOrdered(this.classEntry); - } else { - return Utils.combineHashesOrdered(this.classEntry, this.signature); - } - } - - @Override - public boolean equals(Object other) { - return other instanceof ConstructorEntry && equals((ConstructorEntry) other); - } - - public boolean equals(ConstructorEntry other) { - if (isStatic() != other.isStatic()) { - return false; - } - - if (isStatic()) { - return this.classEntry.equals(other.classEntry); - } else { - return this.classEntry.equals(other.classEntry) && this.signature.equals(other.signature); - } - } - - @Override - public String toString() { - if (isStatic()) { - return this.classEntry.getName() + "." + getName(); - } else { - return this.classEntry.getName() + "." + getName() + this.signature; - } - } -} diff --git a/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java new file mode 100644 index 0000000..1283267 --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/DirectionalTranslator.java @@ -0,0 +1,319 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.bytecode.AccessFlags; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class DirectionalTranslator implements Translator { + + private final TranslationDirection direction; + private final Map classes; + private final TranslationIndex index; + + public DirectionalTranslator(ReferencedEntryPool entryPool) { + this.direction = null; + this.classes = Maps.newHashMap(); + this.index = new TranslationIndex(entryPool); + } + + public DirectionalTranslator(TranslationDirection direction, Map classes, TranslationIndex index) { + this.direction = direction; + this.classes = classes; + this.index = index; + } + + public TranslationDirection getDirection() { + return direction; + } + + public TranslationIndex getTranslationIndex() { + return index; + } + + @Override + public ClassEntry getTranslatedClass(ClassEntry entry) { + String className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry); + return new ClassEntry(className); + } + + @Override + public ClassDefEntry getTranslatedClassDef(ClassDefEntry entry) { + String className = entry.isInnerClass() ? translateInnerClassName(entry) : translateClassName(entry); + return new ClassDefEntry(className, getClassModifier(entry).transform(entry.getAccess())); + } + + private String translateClassName(ClassEntry entry) { + // normal classes are easy + ClassMapping classMapping = this.classes.get(entry.getName()); + if (classMapping == null) { + return entry.getName(); + } + return classMapping.getTranslatedName(direction); + } + + private String translateInnerClassName(ClassEntry entry) { + // translate as much of the class chain as we can + List mappingsChain = getClassMappingChain(entry); + String[] obfClassNames = entry.getName().split("\\$"); + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < obfClassNames.length; i++) { + boolean isFirstClass = buf.length() == 0; + String className = null; + ClassMapping classMapping = mappingsChain.get(i); + if (classMapping != null) { + className = this.direction.choose( + classMapping.getDeobfName(), + isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() + ); + } + if (className == null) { + className = obfClassNames[i]; + } + if (!isFirstClass) { + buf.append("$"); + } + buf.append(className); + } + return buf.toString(); + } + + @Override + public FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry) { + String translatedName = translateFieldName(entry); + if (translatedName == null) { + return entry; + } + ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); + TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc()); + AccessFlags translatedAccess = getFieldModifier(entry).transform(entry.getAccess()); + return new FieldDefEntry(translatedOwner, translatedName, translatedDesc, translatedAccess); + } + + @Override + public FieldEntry getTranslatedField(FieldEntry entry) { + String translatedName = translateFieldName(entry); + if (translatedName == null) { + return null; + } + ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); + TypeDescriptor translatedDesc = getTranslatedTypeDesc(entry.getDesc()); + return new FieldEntry(translatedOwner, translatedName, translatedDesc); + } + + private String translateFieldName(FieldEntry entry) { + // resolve the class entry + ClassEntry resolvedClassEntry = this.index.resolveEntryOwner(entry); + if (resolvedClassEntry != null) { + // look for the class + ClassMapping classMapping = findClassMapping(resolvedClassEntry); + if (classMapping != null) { + // look for the field + FieldMapping mapping = classMapping.getFieldByObf(entry.getName(), entry.getDesc()); + if (mapping != null) { + return this.direction.choose(mapping.getDeobfName(), mapping.getObfName()); + } + } + } + return null; + } + + @Override + public MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry) { + String translatedName = translateMethodName(entry); + if (translatedName == null) { + return entry; + } + ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); + MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc()); + AccessFlags access = getMethodModifier(entry).transform(entry.getAccess()); + return new MethodDefEntry(translatedOwner, translatedName, translatedDesc, access); + } + + @Override + public MethodEntry getTranslatedMethod(MethodEntry entry) { + String translatedName = translateMethodName(entry); + if (translatedName == null) { + return null; + } + ClassEntry translatedOwner = getTranslatedClass(entry.getOwnerClassEntry()); + MethodDescriptor translatedDesc = getTranslatedMethodDesc(entry.getDesc()); + return new MethodEntry(translatedOwner, translatedName, translatedDesc); + } + + private String translateMethodName(MethodEntry entry) { + // resolve the class entry + ClassEntry resolvedOwner = this.index.resolveEntryOwner(entry, true); + if (resolvedOwner != null) { + // look for class + ClassMapping classMapping = findClassMapping(resolvedOwner); + if (classMapping != null) { + // look for the method + MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc()); + if (mapping != null) { + return this.direction.choose(mapping.getDeobfName(), mapping.getObfName()); + } + } + } + return null; + } + + @Override + public LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry) { + String translatedArgumentName = translateLocalVariableName(entry); + if (translatedArgumentName == null) { + translatedArgumentName = inheritLocalVariableName(entry); + } + if (translatedArgumentName == null) { + return null; + } + // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this? + MethodEntry translatedOwner = getTranslatedMethod(entry.getOwnerEntry()); + return new LocalVariableEntry(translatedOwner, entry.getIndex(), translatedArgumentName); + } + + @Override + public LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry) { + String translatedArgumentName = translateLocalVariableName(entry); + if (translatedArgumentName == null) { + translatedArgumentName = inheritLocalVariableName(entry); + } + if (translatedArgumentName == null) { + return entry; + } + // TODO: Translating arguments calls method translation.. Can we refactor the code in such a way that we don't need this? + MethodDefEntry translatedOwner = getTranslatedMethodDef(entry.getOwnerEntry()); + TypeDescriptor translatedTypeDesc = getTranslatedTypeDesc(entry.getDesc()); + return new LocalVariableDefEntry(translatedOwner, entry.getIndex(), translatedArgumentName, translatedTypeDesc); + } + + // TODO: support not identical behavior (specific to constructor) + private String translateLocalVariableName(LocalVariableEntry entry) { + // look for identical behavior in superclasses + ClassEntry ownerEntry = entry.getOwnerClassEntry(); + if (ownerEntry != null) { + // look for the class + ClassMapping classMapping = findClassMapping(ownerEntry); + if (classMapping != null) { + // look for the method + MethodMapping methodMapping = classMapping.getMethodByObf(entry.getMethodName(), entry.getMethodDesc()); + if (methodMapping != null) { + int index = entry.getIndex(); + return this.direction.choose( + methodMapping.getDeobfLocalVariableName(index), + methodMapping.getObfLocalVariableName(index) + ); + } + } + } + return null; + } + + private String inheritLocalVariableName(LocalVariableEntry entry) { + List ancestry = this.index.getAncestry(entry.getOwnerClassEntry()); + // Check in mother class for the arg + for (ClassEntry ancestorEntry : ancestry) { + LocalVariableEntry motherArg = entry.updateOwnership(ancestorEntry); + if (this.index.entryExists(motherArg)) { + String result = translateLocalVariableName(motherArg); + if (result != null) { + return result; + } + } + } + return null; + } + + @Override + public TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc) { + return desc.remap(name -> getTranslatedClass(new ClassEntry(name)).getName()); + } + + @Override + public MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor) { + List arguments = descriptor.getArgumentDescs(); + List translatedArguments = new ArrayList<>(arguments.size()); + for (TypeDescriptor argument : arguments) { + translatedArguments.add(getTranslatedTypeDesc(argument)); + } + return new MethodDescriptor(translatedArguments, getTranslatedTypeDesc(descriptor.getReturnDesc())); + } + + private ClassMapping findClassMapping(ClassEntry entry) { + List mappingChain = getClassMappingChain(entry); + return mappingChain.get(mappingChain.size() - 1); + } + + private List getClassMappingChain(ClassEntry entry) { + + // get a list of all the classes in the hierarchy + String[] parts = entry.getName().split("\\$"); + List mappingsChain = Lists.newArrayList(); + + // get mappings for the outer class + ClassMapping outerClassMapping = this.classes.get(parts[0]); + mappingsChain.add(outerClassMapping); + + for (int i = 1; i < parts.length; i++) { + + // get mappings for the inner class + ClassMapping innerClassMapping = null; + if (outerClassMapping != null) { + innerClassMapping = this.direction.choose( + outerClassMapping.getInnerClassByObfSimple(parts[i]), + outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) + ); + } + mappingsChain.add(innerClassMapping); + outerClassMapping = innerClassMapping; + } + + assert (mappingsChain.size() == parts.length); + return mappingsChain; + } + + private Mappings.EntryModifier getClassModifier(ClassEntry entry) { + ClassMapping classMapping = findClassMapping(entry); + if (classMapping != null) { + return classMapping.getModifier(); + } + return Mappings.EntryModifier.UNCHANGED; + } + + private Mappings.EntryModifier getFieldModifier(FieldEntry entry) { + ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry()); + if (classMapping != null) { + FieldMapping fieldMapping = classMapping.getFieldByObf(entry); + if (fieldMapping != null) { + return fieldMapping.getModifier(); + } + } + return Mappings.EntryModifier.UNCHANGED; + } + + private Mappings.EntryModifier getMethodModifier(MethodEntry entry) { + ClassMapping classMapping = findClassMapping(entry.getOwnerClassEntry()); + if (classMapping != null) { + MethodMapping methodMapping = classMapping.getMethodByObf(entry); + if (methodMapping != null) { + return methodMapping.getModifier(); + } + } + return Mappings.EntryModifier.UNCHANGED; + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/Entry.java b/src/main/java/cuchaz/enigma/mapping/Entry.java index c79510b..eb783e9 100644 --- a/src/main/java/cuchaz/enigma/mapping/Entry.java +++ b/src/main/java/cuchaz/enigma/mapping/Entry.java @@ -16,7 +16,7 @@ public interface Entry { String getClassName(); - ClassEntry getClassEntry(); + ClassEntry getOwnerClassEntry(); - Entry cloneToNewClass(ClassEntry classEntry); + Entry updateOwnership(ClassEntry classEntry); } diff --git a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java index 993bb64..c20f6f5 100644 --- a/src/main/java/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/main/java/cuchaz/enigma/mapping/EntryFactory.java @@ -12,19 +12,8 @@ package cuchaz.enigma.mapping; import cuchaz.enigma.analysis.JarIndex; -import javassist.*; -import javassist.bytecode.Descriptor; -import javassist.expr.ConstructorCall; -import javassist.expr.FieldAccess; -import javassist.expr.MethodCall; -import javassist.expr.NewExpr; public class EntryFactory { - - public static ClassEntry getClassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getName())); - } - public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); @@ -38,95 +27,19 @@ public class EntryFactory { return new ClassEntry(classMapping.getDeobfName()); } - public static ClassEntry getSuperclassEntry(CtClass c) { - return new ClassEntry(Descriptor.toJvmName(c.getClassFile().getSuperclass())); - } - - public static FieldEntry getFieldEntry(CtField field) { - return new FieldEntry(getClassEntry(field.getDeclaringClass()), field.getName(), new Type(field.getFieldInfo().getDescriptor())); - } - - public static FieldEntry getFieldEntry(FieldAccess call) { - return new FieldEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getFieldName(), new Type(call.getSignature())); - } - - public static FieldEntry getFieldEntry(String className, String name, String type) { - return new FieldEntry(new ClassEntry(className), name, new Type(type)); - } - public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { - return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfType()); - } - - public static MethodEntry getMethodEntry(CtMethod method) { - return new MethodEntry(getClassEntry(method.getDeclaringClass()), method.getName(), new Signature(method.getMethodInfo().getDescriptor())); - } - - public static MethodEntry getMethodEntry(MethodCall call) { - return new MethodEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), call.getMethodName(), new Signature(call.getSignature())); - } - - public static ConstructorEntry getConstructorEntry(CtConstructor constructor) { - if (constructor.isClassInitializer()) { - return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass())); - } else { - return new ConstructorEntry(getClassEntry(constructor.getDeclaringClass()), new Signature(constructor.getMethodInfo().getDescriptor())); - } - } - - public static ConstructorEntry getConstructorEntry(ConstructorCall call) { - return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); - } - - public static ConstructorEntry getConstructorEntry(NewExpr call) { - return new ConstructorEntry(new ClassEntry(Descriptor.toJvmName(call.getClassName())), new Signature(call.getSignature())); - } - - public static BehaviorEntry getBehaviorEntry(CtBehavior behavior) { - if (behavior instanceof CtMethod) { - return getMethodEntry((CtMethod) behavior); - } else if (behavior instanceof CtConstructor) { - return getConstructorEntry((CtConstructor) behavior); - } - throw new Error("behavior is neither Method nor Constructor!"); - } - - public static BehaviorEntry getBehaviorEntry(String className, String behaviorName, String behaviorSignature) { - return getBehaviorEntry(new ClassEntry(className), behaviorName, new Signature(behaviorSignature)); - } - - public static BehaviorEntry getBehaviorEntry(String className, String behaviorName) { - return getBehaviorEntry(new ClassEntry(className), behaviorName); - } - - public static BehaviorEntry getBehaviorEntry(String className) { - return new ConstructorEntry(new ClassEntry(className)); - } - - public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName, Signature behaviorSignature) { - switch (behaviorName) { - case "": - return new ConstructorEntry(classEntry, behaviorSignature); - case "": - return new ConstructorEntry(classEntry); - default: - return new MethodEntry(classEntry, behaviorName, behaviorSignature); - } + return new FieldEntry(getObfClassEntry(classMapping), fieldMapping.getObfName(), fieldMapping.getObfDesc()); } - public static BehaviorEntry getBehaviorEntry(ClassEntry classEntry, String behaviorName) { - if (behaviorName.equals("")) { - return new ConstructorEntry(classEntry); - } else { - throw new IllegalArgumentException("Only class initializers don't have signatures"); - } + public static MethodEntry getMethodEntry(ClassEntry classEntry, String name, MethodDescriptor desc) { + return new MethodEntry(classEntry, name, desc); } - public static BehaviorEntry getObfBehaviorEntry(ClassEntry classEntry, MethodMapping methodMapping) { - return getBehaviorEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfSignature()); + public static MethodEntry getObfMethodEntry(ClassEntry classEntry, MethodMapping methodMapping) { + return getMethodEntry(classEntry, methodMapping.getObfName(), methodMapping.getObfDesc()); } - public static BehaviorEntry getObfBehaviorEntry(ClassMapping classMapping, MethodMapping methodMapping) { - return getObfBehaviorEntry(getObfClassEntry(classMapping), methodMapping); + public static MethodEntry getObfMethodEntry(ClassMapping classMapping, MethodMapping methodMapping) { + return getObfMethodEntry(getObfClassEntry(classMapping), methodMapping); } } diff --git a/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java new file mode 100644 index 0000000..262c16c --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/FieldDefEntry.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +import com.google.common.base.Preconditions; +import cuchaz.enigma.bytecode.AccessFlags; + +public class FieldDefEntry extends FieldEntry { + private final AccessFlags access; + + public FieldDefEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc, AccessFlags access) { + super(ownerEntry, name, desc); + Preconditions.checkNotNull(access, "Field access cannot be null"); + this.access = access; + } + + public AccessFlags getAccess() { + return access; + } + + @Override + public FieldDefEntry updateOwnership(ClassEntry owner) { + return new FieldDefEntry(owner, this.name, this.desc, access); + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java index 0f1f506..c118ac0 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldEntry.java @@ -11,40 +11,29 @@ package cuchaz.enigma.mapping; +import com.google.common.base.Preconditions; import cuchaz.enigma.utils.Utils; public class FieldEntry implements Entry { - private ClassEntry classEntry; - private String name; - private Type type; + protected final ClassEntry ownerEntry; + protected final String name; + protected final TypeDescriptor desc; // NOTE: this argument order is important for the MethodReader/MethodWriter - public FieldEntry(ClassEntry classEntry, String name, Type type) { - if (classEntry == null) { - throw new IllegalArgumentException("Class cannot be null!"); - } - if (name == null) { - throw new IllegalArgumentException("Field name cannot be null!"); - } - if (type == null) { - throw new IllegalArgumentException("Field type cannot be null!"); - } + public FieldEntry(ClassEntry ownerEntry, String name, TypeDescriptor desc) { + Preconditions.checkNotNull(ownerEntry, "Owner cannot be null"); + Preconditions.checkNotNull(name, "Field name cannot be null"); + Preconditions.checkNotNull(desc, "Field descriptor cannot be null"); - this.classEntry = classEntry; + this.ownerEntry = ownerEntry; this.name = name; - this.type = type; - } - - public FieldEntry(FieldEntry other, ClassEntry newClassEntry) { - this.classEntry = newClassEntry; - this.name = other.name; - this.type = other.type; + this.desc = desc; } @Override - public ClassEntry getClassEntry() { - return this.classEntry; + public ClassEntry getOwnerClassEntry() { + return this.ownerEntry; } @Override @@ -54,21 +43,21 @@ public class FieldEntry implements Entry { @Override public String getClassName() { - return this.classEntry.getName(); + return this.ownerEntry.getName(); } - public Type getType() { - return this.type; + public TypeDescriptor getDesc() { + return this.desc; } @Override - public FieldEntry cloneToNewClass(ClassEntry classEntry) { - return new FieldEntry(this, classEntry); + public FieldEntry updateOwnership(ClassEntry owner) { + return new FieldEntry(owner, this.name, this.desc); } @Override public int hashCode() { - return Utils.combineHashesOrdered(this.classEntry, this.name, this.type); + return Utils.combineHashesOrdered(this.ownerEntry, this.name, this.desc); } @Override @@ -77,11 +66,11 @@ public class FieldEntry implements Entry { } public boolean equals(FieldEntry other) { - return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.type.equals(other.type); + return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.desc.equals(other.desc); } @Override public String toString() { - return this.classEntry.getName() + "." + this.name + ":" + this.type; + return this.ownerEntry.getName() + "." + this.name + ":" + this.desc; } } diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java index cd761b4..3c46a37 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java @@ -17,26 +17,19 @@ public class FieldMapping implements Comparable, MemberMapping, MemberMapping, MemberMapping - { + // rename obf classes in the desc + TypeDescriptor newDesc = this.obfDesc.remap(className -> { if (className.equals(oldObfClassName)) { return newObfClassName; } - return null; + return className; }); - if (!newType.equals(this.obfType)) { - this.obfType = newType; + if (!newDesc.equals(this.obfDesc)) { + this.obfDesc = newDesc; return true; } return false; diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java new file mode 100644 index 0000000..cc677c5 --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableDefEntry.java @@ -0,0 +1,75 @@ +package cuchaz.enigma.mapping; + +import com.google.common.base.Preconditions; +import cuchaz.enigma.utils.Utils; + +/** + * TypeDescriptor... + * Created by Thog + * 19/10/2016 + */ +public class LocalVariableDefEntry extends LocalVariableEntry { + + protected final MethodDefEntry ownerEntry; + protected final TypeDescriptor desc; + + public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name, TypeDescriptor desc) { + super(ownerEntry, index, name); + Preconditions.checkNotNull(desc, "Variable desc cannot be null"); + + this.ownerEntry = ownerEntry; + this.desc = desc; + } + + public LocalVariableDefEntry(MethodDefEntry ownerEntry, int index, String name) { + super(ownerEntry, index, name); + + this.ownerEntry = ownerEntry; + + int namedIndex = getNamedIndex(); + if (namedIndex < 0) { + this.desc = TypeDescriptor.of(ownerEntry.getOwnerClassEntry().getName()); + } else { + this.desc = ownerEntry.getDesc().getArgumentDescs().get(namedIndex); + } + } + + @Override + public MethodDefEntry getOwnerEntry() { + return this.ownerEntry; + } + + public TypeDescriptor getDesc() { + return desc; + } + + public int getNamedIndex() { + // If we're not static, "this" is bound to index 0 + int indexOffset = ownerEntry.getAccess().isStatic() ? 0 : 1; + return index - indexOffset; + } + + @Override + public LocalVariableDefEntry updateOwnership(ClassEntry classEntry) { + return new LocalVariableDefEntry(ownerEntry.updateOwnership(classEntry), index, name, desc); + } + + @Override + public int hashCode() { + return Utils.combineHashesOrdered(this.ownerEntry, this.desc.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); + } + + @Override + public boolean equals(Object other) { + return other instanceof LocalVariableDefEntry && equals((LocalVariableDefEntry) other); + } + + public boolean equals(LocalVariableDefEntry other) { + return this.ownerEntry.equals(other.ownerEntry) && this.desc.equals(other.desc) && this.name.equals(other.name) && this.index == other.index; + } + + @Override + public String toString() { + return this.ownerEntry + "(" + this.index + ":" + this.name + ":" + this.desc + ")"; + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java index 2bb5e3f..dcfd0ff 100644 --- a/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableEntry.java @@ -1,52 +1,31 @@ package cuchaz.enigma.mapping; +import com.google.common.base.Preconditions; import cuchaz.enigma.utils.Utils; /** - * Desc... + * TypeDescriptor... * Created by Thog * 19/10/2016 */ public class LocalVariableEntry implements Entry { - protected final BehaviorEntry behaviorEntry; + protected final MethodEntry ownerEntry; protected final String name; - protected final Type type; protected final int index; - public LocalVariableEntry(BehaviorEntry behaviorEntry, int index, String name, Type type) { - if (behaviorEntry == null) { - throw new IllegalArgumentException("Behavior cannot be null!"); - } - if (index < 0) { - throw new IllegalArgumentException("Index must be non-negative!"); - } - if (name == null) { - throw new IllegalArgumentException("Variable name cannot be null!"); - } - if (type == null) { - throw new IllegalArgumentException("Variable type cannot be null!"); - } - - this.behaviorEntry = behaviorEntry; + public LocalVariableEntry(MethodEntry ownerEntry, int index, String name) { + Preconditions.checkNotNull(ownerEntry, "Variable owner cannot be null"); + Preconditions.checkNotNull(name, "Variable name cannot be null"); + Preconditions.checkArgument(index >= 0, "Index must be positive"); + + this.ownerEntry = ownerEntry; this.name = name; - this.type = type; this.index = index; } - public LocalVariableEntry(LocalVariableEntry other, ClassEntry newClassEntry) { - this.behaviorEntry = (BehaviorEntry) other.behaviorEntry.cloneToNewClass(newClassEntry); - this.name = other.name; - this.type = other.type; - this.index = other.index; - } - - public BehaviorEntry getBehaviorEntry() { - return this.behaviorEntry; - } - - public Type getType() { - return type; + public MethodEntry getOwnerEntry() { + return this.ownerEntry; } public int getIndex() { @@ -59,31 +38,31 @@ public class LocalVariableEntry implements Entry { } @Override - public ClassEntry getClassEntry() { - return this.behaviorEntry.getClassEntry(); + public ClassEntry getOwnerClassEntry() { + return this.ownerEntry.getOwnerClassEntry(); } @Override public String getClassName() { - return this.behaviorEntry.getClassName(); + return this.ownerEntry.getClassName(); } @Override - public LocalVariableEntry cloneToNewClass(ClassEntry classEntry) { - return new LocalVariableEntry(this, classEntry); + public LocalVariableEntry updateOwnership(ClassEntry classEntry) { + return new LocalVariableEntry(ownerEntry.updateOwnership(classEntry), index, name); } public String getMethodName() { - return this.behaviorEntry.getName(); + return this.ownerEntry.getName(); } - public Signature getMethodSignature() { - return this.behaviorEntry.getSignature(); + public MethodDescriptor getMethodDesc() { + return this.ownerEntry.getDesc(); } @Override public int hashCode() { - return Utils.combineHashesOrdered(this.behaviorEntry, this.type.hashCode(), this.name.hashCode(), Integer.hashCode(this.index)); + return Utils.combineHashesOrdered(this.ownerEntry, this.name.hashCode(), Integer.hashCode(this.index)); } @Override @@ -92,11 +71,11 @@ public class LocalVariableEntry implements Entry { } public boolean equals(LocalVariableEntry other) { - return this.behaviorEntry.equals(other.behaviorEntry) && this.type.equals(other.type) && this.name.equals(other.name) && this.index == other.index; + return this.ownerEntry.equals(other.ownerEntry) && this.name.equals(other.name) && this.index == other.index; } @Override public String toString() { - return this.behaviorEntry + "(" + this.index + ":" + this.name + ":" + this.type + ")"; + return this.ownerEntry + "(" + this.index + ":" + this.name + ")"; } } diff --git a/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java new file mode 100644 index 0000000..193c566 --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/LocalVariableMapping.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +public class LocalVariableMapping implements Comparable { + + private int index; + private String name; + + // NOTE: this argument order is important for the MethodReader/MethodWriter + public LocalVariableMapping(int index, String name) { + this.index = index; + this.name = NameValidator.validateArgumentName(name); + } + + public LocalVariableMapping(LocalVariableMapping other) { + this.index = other.index; + this.name = other.name; + } + + public int getIndex() { + return this.index; + } + + public String getName() { + return this.name; + } + + public void setName(String val) { + this.name = NameValidator.validateArgumentName(val); + } + + public LocalVariableEntry getObfEntry(MethodEntry methodEntry) { + return new LocalVariableEntry(methodEntry, index, name); + } + + @Override + public int compareTo(LocalVariableMapping other) { + return Integer.compare(this.index, other.index); + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/Mappings.java b/src/main/java/cuchaz/enigma/mapping/Mappings.java index cf78ca3..cc1ec9c 100644 --- a/src/main/java/cuchaz/enigma/mapping/Mappings.java +++ b/src/main/java/cuchaz/enigma/mapping/Mappings.java @@ -15,6 +15,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import cuchaz.enigma.analysis.TranslationIndex; +import cuchaz.enigma.bytecode.AccessFlags; import cuchaz.enigma.throwables.MappingConflict; import java.io.File; @@ -96,11 +97,11 @@ public class Mappings { public Translator getTranslator(TranslationDirection direction, TranslationIndex index) { switch (direction) { - case Deobfuscating: + case DEOBFUSCATING: - return new Translator(direction, this.classesByObf, index); + return new DirectionalTranslator(direction, this.classesByObf, index); - case Obfuscating: + case OBFUSCATING: // fill in the missing deobf class entries with obf entries Map classes = Maps.newHashMap(); @@ -114,9 +115,9 @@ public class Mappings { // translate the translation index // NOTE: this isn't actually recursive - TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.Deobfuscating, index)); + TranslationIndex deobfIndex = new TranslationIndex(index, getTranslator(TranslationDirection.DEOBFUSCATING, index)); - return new Translator(direction, classes, deobfIndex); + return new DirectionalTranslator(direction, classes, deobfIndex); default: throw new Error("Invalid translation direction!"); @@ -151,9 +152,9 @@ public class Mappings { // add classes from method signatures for (MethodMapping methodMapping : classMapping.methods()) { - for (Type type : methodMapping.getObfSignature().types()) { - if (type.hasClass()) { - classNames.add(type.getClassEntry().getClassName()); + for (TypeDescriptor desc : methodMapping.getObfDesc().types()) { + if (desc.containsType()) { + classNames.add(desc.getOwnerEntry().getClassName()); } } } @@ -165,9 +166,9 @@ public class Mappings { return this.classesByDeobf.containsKey(deobfName); } - public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, Type obfType) { + public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName, TypeDescriptor obfDesc) { ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); - return classMapping != null && classMapping.containsDeobfField(deobfName, obfType); + return classMapping != null && classMapping.containsDeobfField(deobfName, obfDesc); } public boolean containsDeobfField(ClassEntry obfClassEntry, String deobfName) { @@ -180,14 +181,14 @@ public class Mappings { return false; } - public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, Signature obfSignature) { + public boolean containsDeobfMethod(ClassEntry obfClassEntry, String deobfName, MethodDescriptor obfDescriptor) { ClassMapping classMapping = this.classesByObf.get(obfClassEntry.getName()); - return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfSignature); + return classMapping != null && classMapping.containsDeobfMethod(deobfName, obfDescriptor); } - public boolean containsArgument(BehaviorEntry obfBehaviorEntry, String name) { - ClassMapping classMapping = this.classesByObf.get(obfBehaviorEntry.getClassName()); - return classMapping != null && classMapping.containsArgument(obfBehaviorEntry, name); + public boolean containsArgument(MethodEntry obfMethodEntry, String name) { + ClassMapping classMapping = this.classesByObf.get(obfMethodEntry.getClassName()); + return classMapping != null && classMapping.containsArgument(obfMethodEntry, name); } public List getClassMappingChain(ClassEntry obfClass) { @@ -239,5 +240,19 @@ public class Mappings { public String getFormattedName() { return " ACC:" + super.toString(); } + + public AccessFlags transform(AccessFlags access) { + switch (this) { + case PUBLIC: + return access.setPublic(); + case PROTECTED: + return access.setProtected(); + case PRIVATE: + return access.setPrivate(); + case UNCHANGED: + default: + return access; + } + } } } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java index 172641b..4d5be2f 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsChecker.java @@ -23,7 +23,7 @@ public class MappingsChecker { private Map droppedClassMappings; private Map droppedInnerClassMappings; private Map droppedFieldMappings; - private Map droppedMethodMappings; + private Map droppedMethodMappings; public MappingsChecker(JarIndex index) { this.index = index; @@ -45,7 +45,7 @@ public class MappingsChecker { return this.droppedFieldMappings; } - public Map getDroppedMethodMappings() { + public Map getDroppedMethodMappings() { return this.droppedMethodMappings; } @@ -77,10 +77,10 @@ public class MappingsChecker { // check methods for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { - BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); - if (!this.index.containsObfBehavior(obfBehaviorEntry)) { + MethodEntry obfMethodEntry = EntryFactory.getObfMethodEntry(classEntry, methodMapping); + if (!this.index.containsObfMethod(obfMethodEntry)) { classMapping.removeMethodMapping(methodMapping); - this.droppedMethodMappings.put(obfBehaviorEntry, methodMapping); + this.droppedMethodMappings.put(obfMethodEntry, methodMapping); } } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java index a0d4313..d1d5634 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaReader.java @@ -123,8 +123,8 @@ public class MappingsEnigmaReader { return mappings; } - private ArgumentMapping readArgument(String[] parts) { - return new ArgumentMapping(Integer.parseInt(parts[1]), parts[2]); + private LocalVariableMapping readArgument(String[] parts) { + return new LocalVariableMapping(Integer.parseInt(parts[1]), parts[2]); } private ClassMapping readClass(String[] parts, boolean makeSimple) { @@ -150,27 +150,27 @@ public class MappingsEnigmaReader { if (parts.length == 4) { boolean access = parts[3].startsWith("ACC:"); if (access) - mapping = new FieldMapping(parts[1], new Type(parts[2]), null, + mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); else - mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); + mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.UNCHANGED); } else if (parts.length == 5) - mapping = new FieldMapping(parts[1], new Type(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); + mapping = new FieldMapping(parts[1], new TypeDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); return mapping; } private MethodMapping readMethod(String[] parts) { MethodMapping mapping = null; if (parts.length == 3) - mapping = new MethodMapping(parts[1], new Signature(parts[2])); + mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2])); else if (parts.length == 4) { boolean access = parts[3].startsWith("ACC:"); if (access) - mapping = new MethodMapping(parts[1], new Signature(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); + mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[2]), null, Mappings.EntryModifier.valueOf(parts[3].substring(4))); else - mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2]); + mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2]); } else if (parts.length == 5) - mapping = new MethodMapping(parts[1], new Signature(parts[3]), parts[2], + mapping = new MethodMapping(parts[1], new MethodDescriptor(parts[3]), parts[2], Mappings.EntryModifier.valueOf(parts[4].substring(4))); return mapping; } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java index ba1b258..1929977 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsEnigmaWriter.java @@ -127,29 +127,29 @@ public class MappingsEnigmaWriter { private void write(PrintWriter out, FieldMapping fieldMapping, int depth) { if (fieldMapping.getDeobfName() == null) - out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfType().toString(), + out.format("%sFIELD %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getObfDesc().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); else - out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfType().toString(), + out.format("%sFIELD %s %s %s%s\n", getIndent(depth), fieldMapping.getObfName(), fieldMapping.getDeobfName(), fieldMapping.getObfDesc().toString(), fieldMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : fieldMapping.getModifier().getFormattedName()); } private void write(PrintWriter out, MethodMapping methodMapping, int depth) throws IOException { if (methodMapping.getDeobfName() == null) { - out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfSignature(), + out.format("%sMETHOD %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getObfDesc(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); } else { - out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfSignature(), + out.format("%sMETHOD %s %s %s%s\n", getIndent(depth), methodMapping.getObfName(), methodMapping.getDeobfName(), methodMapping.getObfDesc(), methodMapping.getModifier() == Mappings.EntryModifier.UNCHANGED ? "" : methodMapping.getModifier().getFormattedName()); } - for (ArgumentMapping argumentMapping : sorted(methodMapping.arguments())) { - write(out, argumentMapping, depth + 1); + for (LocalVariableMapping localVariableMapping : sorted(methodMapping.arguments())) { + write(out, localVariableMapping, depth + 1); } } - private void write(PrintWriter out, ArgumentMapping argumentMapping, int depth) { - out.format("%sARG %d %s\n", getIndent(depth), argumentMapping.getIndex(), argumentMapping.getName()); + private void write(PrintWriter out, LocalVariableMapping localVariableMapping, int depth) { + out.format("%sARG %d %s\n", getIndent(depth), localVariableMapping.getIndex(), localVariableMapping.getName()); } private > List sorted(Iterable classes) { diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java index 7126d2b..e215a0f 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsRenamer.java @@ -25,12 +25,14 @@ import java.util.zip.GZIPOutputStream; public class MappingsRenamer { - private JarIndex index; + private final JarIndex index; + private final ReferencedEntryPool entryPool; private Mappings mappings; - public MappingsRenamer(JarIndex index, Mappings mappings) { + public MappingsRenamer(JarIndex index, Mappings mappings, ReferencedEntryPool entryPool) { this.index = index; this.mappings = mappings; + this.entryPool = entryPool; } public void setMappings(Mappings mappings) { @@ -46,7 +48,7 @@ public class MappingsRenamer { if (deobfName != null) { // make sure we don't rename to an existing obf or deobf class - if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(new ClassEntry(deobfName))) { + if (mappings.containsDeobfClass(deobfName) || index.containsObfClass(entryPool.getClass(deobfName))) { throw new IllegalNameException(deobfName, "There is already a class with that name"); } } @@ -87,13 +89,13 @@ public class MappingsRenamer { public void setFieldName(FieldEntry obf, String deobfName) { deobfName = NameValidator.validateFieldName(deobfName); - FieldEntry targetEntry = new FieldEntry(obf.getClassEntry(), deobfName, obf.getType()); + FieldEntry targetEntry = entryPool.getField(obf.getOwnerClassEntry(), deobfName, obf.getDesc()); ClassEntry definedClass = null; - if (mappings.containsDeobfField(obf.getClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) - definedClass = obf.getClassEntry(); + if (mappings.containsDeobfField(obf.getOwnerClassEntry(), deobfName) || index.containsEntryWithSameName(targetEntry)) + definedClass = obf.getOwnerClassEntry(); else { - for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getClassEntry())) { - if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.cloneToNewClass(ancestorEntry))) { + for (ClassEntry ancestorEntry : this.index.getTranslationIndex().getAncestry(obf.getOwnerClassEntry())) { + if (mappings.containsDeobfField(ancestorEntry, deobfName) || index.containsEntryWithSameName(targetEntry.updateOwnership(ancestorEntry))) { definedClass = ancestorEntry; break; } @@ -101,42 +103,44 @@ public class MappingsRenamer { } if (definedClass != null) { - String className = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(definedClass.getClassName()); + Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex()); + String className = translator.getTranslatedClass(entryPool.getClass(definedClass.getClassName())).getName(); if (className == null) className = definedClass.getClassName(); throw new IllegalNameException(deobfName, "There is already a field with that name in " + className); } - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), obf.getType(), deobfName); + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + classMapping.setFieldName(obf.getName(), obf.getDesc(), deobfName); } public void removeFieldMapping(FieldEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getType())); + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + classMapping.removeFieldMapping(classMapping.getFieldByObf(obf.getName(), obf.getDesc())); } public void markFieldAsDeobfuscated(FieldEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setFieldName(obf.getName(), obf.getType(), obf.getName()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + classMapping.setFieldName(obf.getName(), obf.getDesc(), obf.getName()); } private void validateMethodTreeName(MethodEntry entry, String deobfName) { - MethodEntry targetEntry = new MethodEntry(entry.getClassEntry(), deobfName, entry.getSignature()); + MethodEntry targetEntry = entryPool.getMethod(entry.getOwnerClassEntry(), deobfName, entry.getDesc()); // TODO: Verify if I don't break things - ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); - if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getSignature()) && classMapping.getMethodByObf(entry.getName(), entry.getSignature()) != classMapping.getMethodByDeobf(deobfName, entry.getSignature())) - || index.containsObfBehavior(targetEntry)) { - String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(entry.getClassName()); + ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry()); + if ((classMapping != null && classMapping.containsDeobfMethod(deobfName, entry.getDesc()) && classMapping.getMethodByObf(entry.getName(), entry.getDesc()) != classMapping.getMethodByDeobf(deobfName, entry.getDesc())) + || index.containsObfMethod(targetEntry)) { + Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex()); + String deobfClassName = translator.getTranslatedClass(entryPool.getClass(entry.getClassName())).getClassName(); if (deobfClassName == null) { deobfClassName = entry.getClassName(); } throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); } - for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getClassEntry())) { - validateMethodTreeName(entry.cloneToNewClass(child), deobfName); + for (ClassEntry child : index.getTranslationIndex().getSubclass(entry.getOwnerClassEntry())) { + validateMethodTreeName(entry.updateOwnership(child), deobfName); } } @@ -155,20 +159,21 @@ public class MappingsRenamer { public void setMethodName(MethodEntry obf, String deobfName) { deobfName = NameValidator.validateMethodName(deobfName); - MethodEntry targetEntry = new MethodEntry(obf.getClassEntry(), deobfName, obf.getSignature()); - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); + MethodEntry targetEntry = entryPool.getMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); // TODO: Verify if I don't break things - if ((mappings.containsDeobfMethod(obf.getClassEntry(), deobfName, obf.getSignature()) && classMapping.getMethodByObf(obf.getName(), obf.getSignature()) != classMapping.getMethodByDeobf(deobfName, obf.getSignature())) - || index.containsObfBehavior(targetEntry)) { - String deobfClassName = mappings.getTranslator(TranslationDirection.Deobfuscating, index.getTranslationIndex()).translateClass(obf.getClassName()); + if ((mappings.containsDeobfMethod(obf.getOwnerClassEntry(), deobfName, obf.getDesc()) && classMapping.getMethodByObf(obf.getName(), obf.getDesc()) != classMapping.getMethodByDeobf(deobfName, obf.getDesc())) + || index.containsObfMethod(targetEntry)) { + Translator translator = mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index.getTranslationIndex()); + String deobfClassName = translator.getTranslatedClass(entryPool.getClass(obf.getClassName())).getClassName(); if (deobfClassName == null) { deobfClassName = obf.getClassName(); } throw new IllegalNameException(deobfName, "There is already a method with that name and signature in class " + deobfClassName); } - classMapping.setMethodName(obf.getName(), obf.getSignature(), deobfName); + classMapping.setMethodName(obf.getName(), obf.getDesc(), deobfName); } public void removeMethodTreeMapping(MethodEntry obf) { @@ -176,8 +181,8 @@ public class MappingsRenamer { } public void removeMethodMapping(MethodEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setMethodName(obf.getName(), obf.getSignature(), null); + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getDesc(), null); } public void markMethodTreeAsDeobfuscated(MethodEntry obf) { @@ -185,30 +190,25 @@ public class MappingsRenamer { } public void markMethodAsDeobfuscated(MethodEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setMethodName(obf.getName(), obf.getSignature(), obf.getName()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + classMapping.setMethodName(obf.getName(), obf.getDesc(), obf.getName()); } - public void setArgumentTreeName(ArgumentEntry obf, String deobfName) { - if (!(obf.getBehaviorEntry() instanceof MethodEntry)) { - setArgumentName(obf, deobfName); - return; - } - - MethodEntry obfMethod = (MethodEntry) obf.getBehaviorEntry(); + public void setLocalVariableTreeName(LocalVariableEntry obf, String deobfName) { + MethodEntry obfMethod = obf.getOwnerEntry(); Set implementations = index.getRelatedMethodImplementations(obfMethod); for (MethodEntry entry : implementations) { - ClassMapping classMapping = mappings.getClassByObf(entry.getClassEntry()); + ClassMapping classMapping = mappings.getClassByObf(entry.getOwnerClassEntry()); if (classMapping != null) { - MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getSignature()); + MethodMapping mapping = classMapping.getMethodByObf(entry.getName(), entry.getDesc()); // NOTE: don't need to check arguments for name collisions with names determined by Procyon // TODO: Verify if I don't break things if (mapping != null) { - for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { - if (argumentMapping.getIndex() != obf.getIndex()) { - if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) - || argumentMapping.getName().equals(deobfName)) { + for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) { + if (localVariableMapping.getIndex() != obf.getIndex()) { + if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName) + || localVariableMapping.getName().equals(deobfName)) { throw new IllegalNameException(deobfName, "There is already an argument with that name"); } } @@ -218,45 +218,45 @@ public class MappingsRenamer { } for (MethodEntry entry : implementations) { - setArgumentName(new ArgumentEntry(obf, entry), deobfName); + setLocalVariableName(new LocalVariableEntry(entry, obf.getIndex(), obf.getName()), deobfName); } } - public void setArgumentName(ArgumentEntry obf, String deobfName) { + public void setLocalVariableName(LocalVariableEntry obf, String deobfName) { deobfName = NameValidator.validateArgumentName(deobfName); - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodSignature()); + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + MethodMapping mapping = classMapping.getMethodByObf(obf.getMethodName(), obf.getMethodDesc()); // NOTE: don't need to check arguments for name collisions with names determined by Procyon // TODO: Verify if I don't break things if (mapping != null) { - for (ArgumentMapping argumentMapping : Lists.newArrayList(mapping.arguments())) { - if (argumentMapping.getIndex() != obf.getIndex()) { - if (mapping.getDeobfArgumentName(argumentMapping.getIndex()).equals(deobfName) - || argumentMapping.getName().equals(deobfName)) { + for (LocalVariableMapping localVariableMapping : Lists.newArrayList(mapping.arguments())) { + if (localVariableMapping.getIndex() != obf.getIndex()) { + if (mapping.getDeobfLocalVariableName(localVariableMapping.getIndex()).equals(deobfName) + || localVariableMapping.getName().equals(deobfName)) { throw new IllegalNameException(deobfName, "There is already an argument with that name"); } } } } - classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), deobfName); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), deobfName); } - public void removeArgumentMapping(ArgumentEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex()); + public void removeLocalVariableMapping(LocalVariableEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + classMapping.removeArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex()); } - public void markArgumentAsDeobfuscated(ArgumentEntry obf) { - ClassMapping classMapping = getOrCreateClassMapping(obf.getClassEntry()); - classMapping.setArgumentName(obf.getMethodName(), obf.getMethodSignature(), obf.getIndex(), obf.getName()); + public void markArgumentAsDeobfuscated(LocalVariableEntry obf) { + ClassMapping classMapping = getOrCreateClassMapping(obf.getOwnerClassEntry()); + classMapping.setArgumentName(obf.getMethodName(), obf.getMethodDesc(), obf.getIndex(), obf.getName()); } public boolean moveFieldToObfClass(ClassMapping classMapping, FieldMapping fieldMapping, ClassEntry obfClass) { classMapping.removeFieldMapping(fieldMapping); ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); - if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfType())) { - if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfType())) { + if (!targetClassMapping.containsObfField(fieldMapping.getObfName(), fieldMapping.getObfDesc())) { + if (!targetClassMapping.containsDeobfField(fieldMapping.getDeobfName(), fieldMapping.getObfDesc())) { targetClassMapping.addFieldMapping(fieldMapping); return true; } else { @@ -269,12 +269,12 @@ public class MappingsRenamer { public boolean moveMethodToObfClass(ClassMapping classMapping, MethodMapping methodMapping, ClassEntry obfClass) { classMapping.removeMethodMapping(methodMapping); ClassMapping targetClassMapping = getOrCreateClassMapping(obfClass); - if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfSignature())) { - if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfSignature())) { + if (!targetClassMapping.containsObfMethod(methodMapping.getObfName(), methodMapping.getObfDesc())) { + if (!targetClassMapping.containsDeobfMethod(methodMapping.getDeobfName(), methodMapping.getObfDesc())) { targetClassMapping.addMethodMapping(methodMapping); return true; } else { - System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfSignature()); + System.err.println("WARNING: deobf method was already there: " + obfClass + "." + methodMapping.getDeobfName() + methodMapping.getObfDesc()); } } return false; @@ -326,12 +326,35 @@ public class MappingsRenamer { } public void setFieldModifier(FieldEntry obEntry, Mappings.EntryModifier modifier) { - ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); - classMapping.setFieldModifier(obEntry.getName(), obEntry.getType(), modifier); + ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry()); + classMapping.setFieldModifier(obEntry.getName(), obEntry.getDesc(), modifier); + } + + public void setMethodModifier(MethodEntry obEntry, Mappings.EntryModifier modifier) { + ClassMapping classMapping = getOrCreateClassMapping(obEntry.getOwnerClassEntry()); + classMapping.setMethodModifier(obEntry.getName(), obEntry.getDesc(), modifier); + } + + public Mappings.EntryModifier getClassModifier(ClassEntry obfEntry) { + ClassMapping classMapping = getOrCreateClassMapping(obfEntry); + return classMapping.getModifier(); } - public void setMethodModifier(BehaviorEntry obEntry, Mappings.EntryModifier modifier) { - ClassMapping classMapping = getOrCreateClassMapping(obEntry.getClassEntry()); - classMapping.setMethodModifier(obEntry.getName(), obEntry.getSignature(), modifier); + public Mappings.EntryModifier getFieldModifier(FieldEntry obfEntry) { + ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry()); + FieldMapping fieldMapping = classMapping.getFieldByObf(obfEntry); + if (fieldMapping == null) { + return Mappings.EntryModifier.UNCHANGED; + } + return fieldMapping.getModifier(); + } + + public Mappings.EntryModifier getMethodModfifier(MethodEntry obfEntry) { + ClassMapping classMapping = getOrCreateClassMapping(obfEntry.getOwnerClassEntry()); + MethodMapping methodMapping = classMapping.getMethodByObf(obfEntry); + if (methodMapping == null) { + return Mappings.EntryModifier.UNCHANGED; + } + return methodMapping.getModifier(); } } diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java index b0eb826..95daa73 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsSRGWriter.java @@ -19,7 +19,7 @@ public class MappingsSRGWriter { } file.createNewFile(); - TranslationIndex index = new TranslationIndex(); + TranslationIndex index = new TranslationIndex(new ReferencedEntryPool()); PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), Charsets.UTF_8)); List fieldMappings = new ArrayList<>(); @@ -43,7 +43,7 @@ public class MappingsSRGWriter { } for (MethodMapping methodMapping : sorted(innerClassMapping.methods())) { - methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); + methodMappings.add("MD: " + innerClassName + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + innerDeobfClassName + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc())); } } @@ -52,7 +52,7 @@ public class MappingsSRGWriter { } for (MethodMapping methodMapping : sorted(classMapping.methods())) { - methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfSignature() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.Deobfuscating, index).translateSignature(methodMapping.getObfSignature())); + methodMappings.add("MD: " + classMapping.getObfFullName() + "/" + methodMapping.getObfName() + " " + methodMapping.getObfDesc() + " " + classMapping.getDeobfName() + "/" + methodMapping.getDeobfName() + " " + mappings.getTranslator(TranslationDirection.DEOBFUSCATING, index).getTranslatedMethodDesc(methodMapping.getObfDesc())); } } for (String fd : fieldMappings) { diff --git a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java index dfe9e88..e635fa1 100644 --- a/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java +++ b/src/main/java/cuchaz/enigma/mapping/MappingsTinyReader.java @@ -20,11 +20,11 @@ public class MappingsTinyReader { } public FieldMapping readField(String[] parts) { - return new FieldMapping(parts[3], new Type(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); + return new FieldMapping(parts[3], new TypeDescriptor(parts[2]), parts[4], Mappings.EntryModifier.UNCHANGED); } public MethodMapping readMethod(String[] parts) { - return new MethodMapping(parts[3], new Signature(parts[2]), parts[4]); + return new MethodMapping(parts[3], new MethodDescriptor(parts[2]), parts[4]); } public Mappings read(File file) throws IOException, MappingParseException { diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java new file mode 100644 index 0000000..d6a160d --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/MethodDefEntry.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +import com.google.common.base.Preconditions; +import cuchaz.enigma.bytecode.AccessFlags; + +public class MethodDefEntry extends MethodEntry { + + private final AccessFlags access; + + public MethodDefEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor, AccessFlags access) { + super(classEntry, name, descriptor); + Preconditions.checkNotNull(access, "Method access cannot be null"); + this.access = access; + } + + public AccessFlags getAccess() { + return access; + } + + @Override + public MethodDefEntry updateOwnership(ClassEntry classEntry) { + return new MethodDefEntry(new ClassEntry(classEntry.getName()), name, descriptor, access); + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java new file mode 100644 index 0000000..210ada0 --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/MethodDescriptor.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +import com.google.common.collect.Lists; +import cuchaz.enigma.utils.Utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +public class MethodDescriptor { + + private List argumentDescs; + private TypeDescriptor returnDesc; + + public MethodDescriptor(String desc) { + try { + this.argumentDescs = Lists.newArrayList(); + int i = 0; + while (i < desc.length()) { + char c = desc.charAt(i); + if (c == '(') { + assert (this.argumentDescs.isEmpty()); + assert (this.returnDesc == null); + i++; + } else if (c == ')') { + i++; + break; + } else { + String type = TypeDescriptor.parseFirst(desc.substring(i)); + this.argumentDescs.add(new TypeDescriptor(type)); + i += type.length(); + } + } + this.returnDesc = new TypeDescriptor(TypeDescriptor.parseFirst(desc.substring(i))); + } catch (Exception ex) { + throw new IllegalArgumentException("Unable to parse method descriptor: " + desc, ex); + } + } + + public MethodDescriptor(List argumentDescs, TypeDescriptor returnDesc) { + this.argumentDescs = argumentDescs; + this.returnDesc = returnDesc; + } + + public List getArgumentDescs() { + return this.argumentDescs; + } + + public TypeDescriptor getReturnDesc() { + return this.returnDesc; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append("("); + for (TypeDescriptor desc : this.argumentDescs) { + buf.append(desc); + } + buf.append(")"); + buf.append(this.returnDesc); + return buf.toString(); + } + + public Iterable types() { + List descs = Lists.newArrayList(); + descs.addAll(this.argumentDescs); + descs.add(this.returnDesc); + return descs; + } + + @Override + public boolean equals(Object other) { + return other instanceof MethodDescriptor && equals((MethodDescriptor) other); + } + + public boolean equals(MethodDescriptor other) { + return this.argumentDescs.equals(other.argumentDescs) && this.returnDesc.equals(other.returnDesc); + } + + @Override + public int hashCode() { + return Utils.combineHashesOrdered(this.argumentDescs.hashCode(), this.returnDesc.hashCode()); + } + + public boolean hasClass(ClassEntry classEntry) { + for (TypeDescriptor desc : types()) { + if (desc.containsType() && desc.getOwnerEntry().equals(classEntry)) { + return true; + } + } + return false; + } + + public MethodDescriptor remap(Function remapper) { + List argumentDescs = new ArrayList<>(this.argumentDescs.size()); + for (TypeDescriptor desc : this.argumentDescs) { + argumentDescs.add(desc.remap(remapper)); + } + return new MethodDescriptor(argumentDescs, returnDesc.remap(remapper)); + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java index 9c3058c..f8a5ff1 100644 --- a/src/main/java/cuchaz/enigma/mapping/MethodEntry.java +++ b/src/main/java/cuchaz/enigma/mapping/MethodEntry.java @@ -11,41 +11,27 @@ package cuchaz.enigma.mapping; +import com.google.common.base.Preconditions; import cuchaz.enigma.utils.Utils; -public class MethodEntry implements BehaviorEntry { - - private ClassEntry classEntry; - private String name; - private Signature signature; - - public MethodEntry(ClassEntry classEntry, String name, Signature signature) { - if (classEntry == null) { - throw new IllegalArgumentException("Class cannot be null!"); - } - if (name == null) { - throw new IllegalArgumentException("Method name cannot be null!"); - } - if (signature == null) { - throw new IllegalArgumentException("Method signature cannot be null!"); - } - if (name.startsWith("<")) { - throw new IllegalArgumentException("Don't use MethodEntry for a constructor!"); - } +public class MethodEntry implements Entry { + + protected final ClassEntry classEntry; + protected final String name; + protected final MethodDescriptor descriptor; + + public MethodEntry(ClassEntry classEntry, String name, MethodDescriptor descriptor) { + Preconditions.checkNotNull(classEntry, "Class cannot be null"); + Preconditions.checkNotNull(name, "Method name cannot be null"); + Preconditions.checkNotNull(descriptor, "Method descriptor cannot be null"); this.classEntry = classEntry; this.name = name; - this.signature = signature; - } - - public MethodEntry(MethodEntry other, String newClassName) { - this.classEntry = new ClassEntry(newClassName); - this.name = other.name; - this.signature = other.signature; + this.descriptor = descriptor; } @Override - public ClassEntry getClassEntry() { + public ClassEntry getOwnerClassEntry() { return this.classEntry; } @@ -54,9 +40,12 @@ public class MethodEntry implements BehaviorEntry { return this.name; } - @Override - public Signature getSignature() { - return this.signature; + public MethodDescriptor getDesc() { + return this.descriptor; + } + + public boolean isConstructor() { + return name.equals("") || name.equals(""); } @Override @@ -65,13 +54,13 @@ public class MethodEntry implements BehaviorEntry { } @Override - public MethodEntry cloneToNewClass(ClassEntry classEntry) { - return new MethodEntry(this, classEntry.getName()); + public MethodEntry updateOwnership(ClassEntry classEntry) { + return new MethodEntry(new ClassEntry(classEntry.getName()), name, descriptor); } @Override public int hashCode() { - return Utils.combineHashesOrdered(this.classEntry, this.name, this.signature); + return Utils.combineHashesOrdered(this.classEntry, this.name, this.descriptor); } @Override @@ -80,11 +69,11 @@ public class MethodEntry implements BehaviorEntry { } public boolean equals(MethodEntry other) { - return this.classEntry.equals(other.classEntry) && this.name.equals(other.name) && this.signature.equals(other.signature); + return this.classEntry.equals(other.getOwnerClassEntry()) && this.name.equals(other.getName()) && this.descriptor.equals(other.getDesc()); } @Override public String toString() { - return this.classEntry.getName() + "." + this.name + this.signature; + return this.classEntry.getName() + "." + this.name + this.descriptor; } } diff --git a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java index 1524ce6..2f7fe53 100644 --- a/src/main/java/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/MethodMapping.java @@ -11,50 +11,47 @@ package cuchaz.enigma.mapping; +import com.google.common.base.Preconditions; import com.google.common.collect.Maps; import cuchaz.enigma.throwables.IllegalNameException; import cuchaz.enigma.throwables.MappingConflict; import java.util.Map; -public class MethodMapping implements Comparable, MemberMapping { +public class MethodMapping implements Comparable, MemberMapping { private String obfName; private String deobfName; - private Signature obfSignature; - private Map arguments; + private MethodDescriptor obfDescriptor; + private Map localVariables; private Mappings.EntryModifier modifier; - public MethodMapping(String obfName, Signature obfSignature) { - this(obfName, obfSignature, null, Mappings.EntryModifier.UNCHANGED); + public MethodMapping(String obfName, MethodDescriptor obfDescriptor) { + this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED); } - public MethodMapping(String obfName, Signature obfSignature, String deobfName) { - this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); + public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) { + this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED); } - public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { - if (obfName == null) { - throw new IllegalArgumentException("obf name cannot be null!"); - } - if (obfSignature == null) { - throw new IllegalArgumentException("obf signature cannot be null!"); - } + public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName, Mappings.EntryModifier modifier) { + Preconditions.checkNotNull(obfName, "Method obf name cannot be null"); + Preconditions.checkNotNull(obfDescriptor, "Method obf desc cannot be null"); this.obfName = obfName; this.deobfName = NameValidator.validateMethodName(deobfName); - this.obfSignature = obfSignature; - this.arguments = Maps.newTreeMap(); + this.obfDescriptor = obfDescriptor; + this.localVariables = Maps.newTreeMap(); this.modifier = modifier; } - public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { + public MethodMapping(MethodMapping other, Translator translator) { this.obfName = other.obfName; this.deobfName = other.deobfName; this.modifier = other.modifier; - this.obfSignature = new Signature(other.obfSignature, obfClassNameReplacer); - this.arguments = Maps.newTreeMap(); - for (Map.Entry entry : other.arguments.entrySet()) { - this.arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); + this.obfDescriptor = translator.getTranslatedMethodDesc(other.obfDescriptor); + this.localVariables = Maps.newTreeMap(); + for (Map.Entry entry : other.localVariables.entrySet()) { + this.localVariables.put(entry.getKey(), new LocalVariableMapping(entry.getValue())); } } @@ -84,56 +81,56 @@ public class MethodMapping implements Comparable, MemberMapping arguments() { - return this.arguments.values(); + public Iterable arguments() { + return this.localVariables.values(); } - public void addArgumentMapping(ArgumentMapping argumentMapping) throws MappingConflict { - if (this.arguments.containsKey(argumentMapping.getIndex())) { - throw new MappingConflict("argument", argumentMapping.getName(), this.arguments.get(argumentMapping.getIndex()).getName()); + public void addArgumentMapping(LocalVariableMapping localVariableMapping) throws MappingConflict { + if (this.localVariables.containsKey(localVariableMapping.getIndex())) { + throw new MappingConflict("argument", localVariableMapping.getName(), this.localVariables.get(localVariableMapping.getIndex()).getName()); } - this.arguments.put(argumentMapping.getIndex(), argumentMapping); + this.localVariables.put(localVariableMapping.getIndex(), localVariableMapping); } - public String getObfArgumentName(int index) { - ArgumentMapping argumentMapping = this.arguments.get(index); - if (argumentMapping != null) { - return argumentMapping.getName(); + public String getObfLocalVariableName(int index) { + LocalVariableMapping localVariableMapping = this.localVariables.get(index); + if (localVariableMapping != null) { + return localVariableMapping.getName(); } return null; } - public String getDeobfArgumentName(int index) { - ArgumentMapping argumentMapping = this.arguments.get(index); - if (argumentMapping != null) { - return argumentMapping.getName(); + public String getDeobfLocalVariableName(int index) { + LocalVariableMapping localVariableMapping = this.localVariables.get(index); + if (localVariableMapping != null) { + return localVariableMapping.getName(); } return null; } - public void setArgumentName(int index, String name) { - ArgumentMapping argumentMapping = this.arguments.get(index); - if (argumentMapping == null) { - argumentMapping = new ArgumentMapping(index, name); - boolean wasAdded = this.arguments.put(index, argumentMapping) == null; + public void setLocalVariableName(int index, String name) { + LocalVariableMapping localVariableMapping = this.localVariables.get(index); + if (localVariableMapping == null) { + localVariableMapping = new LocalVariableMapping(index, name); + boolean wasAdded = this.localVariables.put(index, localVariableMapping) == null; assert (wasAdded); } else { - argumentMapping.setName(name); + localVariableMapping.setName(name); } } - public void removeArgumentName(int index) { - boolean wasRemoved = this.arguments.remove(index) != null; + public void removeLocalVariableName(int index) { + boolean wasRemoved = this.localVariables.remove(index) != null; assert (wasRemoved); } @@ -146,14 +143,14 @@ public class MethodMapping implements Comparable, MemberMapping "); - buf.append(argumentMapping.getName()); + buf.append(localVariableMapping.getName()); buf.append("\n"); } return buf.toString(); @@ -161,12 +158,12 @@ public class MethodMapping implements Comparable, MemberMapping, MemberMapping - { + MethodDescriptor newDescriptor = obfDescriptor.remap(className -> { if (className.equals(oldObfClassName)) { return newObfClassName; } - return null; + return className; }); - if (!newSignature.equals(this.obfSignature)) { - this.obfSignature = newSignature; + if (!newDescriptor.equals(this.obfDescriptor)) { + this.obfDescriptor = newDescriptor; return true; } return false; } - public boolean isConstructor() { - return this.obfName.startsWith("<"); - } - @Override - public BehaviorEntry getObfEntry(ClassEntry classEntry) { - if (isConstructor()) { - return new ConstructorEntry(classEntry, this.obfSignature); - } else { - return new MethodEntry(classEntry, this.obfName, this.obfSignature); - } + public MethodEntry getObfEntry(ClassEntry classEntry) { + return new MethodEntry(classEntry, this.obfName, this.obfDescriptor); } public Mappings.EntryModifier getModifier() { diff --git a/src/main/java/cuchaz/enigma/mapping/NameValidator.java b/src/main/java/cuchaz/enigma/mapping/NameValidator.java index aa3dc4d..f178093 100644 --- a/src/main/java/cuchaz/enigma/mapping/NameValidator.java +++ b/src/main/java/cuchaz/enigma/mapping/NameValidator.java @@ -12,7 +12,6 @@ package cuchaz.enigma.mapping; import cuchaz.enigma.throwables.IllegalNameException; -import javassist.bytecode.Descriptor; import java.util.Arrays; import java.util.List; @@ -23,11 +22,11 @@ public class NameValidator { private static final Pattern IdentifierPattern; private static final Pattern ClassPattern; private static final List ReservedWords = Arrays.asList( - "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", - "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", - "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", - "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", - "long", "strictfp", "volatile", "const", "float", "native", "super", "while" + "abstract", "continue", "for", "new", "switch", "assert", "default", "goto", "package", "synchronized", + "boolean", "do", "if", "private", "this", "break", "double", "implements", "protected", "throw", "byte", + "else", "import", "public", "throws", "case", "enum", "instanceof", "return", "transient", "catch", + "extends", "int", "short", "try", "char", "final", "interface", "static", "void", "class", "finally", + "long", "strictfp", "volatile", "const", "float", "native", "super", "while" ); static { @@ -43,10 +42,10 @@ public class NameValidator { if (!ClassPattern.matcher(name).matches() || ReservedWords.contains(name)) { throw new IllegalNameException(name, "This doesn't look like a legal class name"); } - if (packageRequired && new ClassEntry(name).getPackageName() == null) { + if (packageRequired && ClassEntry.getPackageName(name) == null) { throw new IllegalNameException(name, "Class must be in a package"); } - return Descriptor.toJvmName(name); + return name; } public static String validateFieldName(String name) { diff --git a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java index 33d930d..9300656 100644 --- a/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java +++ b/src/main/java/cuchaz/enigma/mapping/ProcyonEntryFactory.java @@ -12,12 +12,18 @@ package cuchaz.enigma.mapping; import com.strobel.assembler.metadata.*; +import cuchaz.enigma.bytecode.AccessFlags; import java.util.List; public class ProcyonEntryFactory { + private final ReferencedEntryPool entryPool; - private static String getErasedSignature(MemberReference def) { + public ProcyonEntryFactory(ReferencedEntryPool entryPool) { + this.entryPool = entryPool; + } + + private String getErasedSignature(MemberReference def) { if (!(def instanceof MethodReference)) return def.getErasedSignature(); MethodReference methodReference = (MethodReference) def; @@ -41,27 +47,23 @@ public class ProcyonEntryFactory { return builder.toString(); } - public static FieldEntry getFieldEntry(MemberReference def) { - return new FieldEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Type(def.getErasedSignature())); + public FieldEntry getFieldEntry(MemberReference def) { + ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); + return entryPool.getField(classEntry, def.getName(), def.getErasedSignature()); } - public static MethodEntry getMethodEntry(MemberReference def) { - return new MethodEntry(new ClassEntry(def.getDeclaringType().getInternalName()), def.getName(), new Signature(getErasedSignature(def))); + public FieldDefEntry getFieldDefEntry(FieldDefinition def) { + ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); + return new FieldDefEntry(classEntry, def.getName(), new TypeDescriptor(def.getErasedSignature()), new AccessFlags(def.getModifiers())); } - public static ConstructorEntry getConstructorEntry(MethodReference def) { - if (def.isTypeInitializer()) { - return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName())); - } else { - return new ConstructorEntry(new ClassEntry(def.getDeclaringType().getInternalName()), new Signature(def.getErasedSignature())); - } + public MethodEntry getMethodEntry(MemberReference def) { + ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); + return entryPool.getMethod(classEntry, def.getName(), getErasedSignature(def)); } - public static BehaviorEntry getBehaviorEntry(MethodReference def) { - if (def.isConstructor() || def.isTypeInitializer()) { - return getConstructorEntry(def); - } else { - return getMethodEntry(def); - } + public MethodDefEntry getMethodDefEntry(MethodDefinition def) { + ClassEntry classEntry = entryPool.getClass(def.getDeclaringType().getInternalName()); + return new MethodDefEntry(classEntry, def.getName(), new MethodDescriptor(def.getErasedSignature()), new AccessFlags(def.getModifiers())); } } diff --git a/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java b/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java new file mode 100644 index 0000000..2abc76c --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/ReferencedEntryPool.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +import java.util.HashMap; +import java.util.Map; + +public class ReferencedEntryPool { + private final Map classEntries = new HashMap<>(); + private final Map> methodEntries = new HashMap<>(); + private final Map> fieldEntries = new HashMap<>(); + + public ClassEntry getClass(String name) { + return this.classEntries.computeIfAbsent(name, s -> new ClassEntry(name)); + } + + public MethodEntry getMethod(ClassEntry ownerEntry, String name, String desc) { + return getMethod(ownerEntry, name, new MethodDescriptor(desc)); + } + + public MethodEntry getMethod(ClassEntry ownerEntry, String name, MethodDescriptor desc) { + String key = name + desc.toString(); + return getClassMethods(ownerEntry.getName()).computeIfAbsent(key, s -> new MethodEntry(ownerEntry, name, desc)); + } + + public FieldEntry getField(ClassEntry ownerEntry, String name, String desc) { + return getField(ownerEntry, name, new TypeDescriptor(desc)); + } + + public FieldEntry getField(ClassEntry ownerEntry, String name, TypeDescriptor desc) { + return getClassFields(ownerEntry.getName()).computeIfAbsent(name, s -> new FieldEntry(ownerEntry, name, desc)); + } + + private Map getClassMethods(String name) { + return methodEntries.computeIfAbsent(name, s -> new HashMap<>()); + } + + private Map getClassFields(String name) { + return fieldEntries.computeIfAbsent(name, s -> new HashMap<>()); + } +} diff --git a/src/main/java/cuchaz/enigma/mapping/Signature.java b/src/main/java/cuchaz/enigma/mapping/Signature.java deleted file mode 100644 index 78130d6..0000000 --- a/src/main/java/cuchaz/enigma/mapping/Signature.java +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.mapping; - -import com.google.common.collect.Lists; -import cuchaz.enigma.utils.Utils; - -import java.util.List; - -public class Signature { - - private List argumentTypes; - private Type returnType; - - public Signature(String signature) { - try { - this.argumentTypes = Lists.newArrayList(); - int i = 0; - while (i < signature.length()) { - char c = signature.charAt(i); - if (c == '(') { - assert (this.argumentTypes.isEmpty()); - assert (this.returnType == null); - i++; - } else if (c == ')') { - i++; - break; - } else { - String type = Type.parseFirst(signature.substring(i)); - this.argumentTypes.add(new Type(type)); - i += type.length(); - } - } - this.returnType = new Type(Type.parseFirst(signature.substring(i))); - } catch (Exception ex) { - throw new IllegalArgumentException("Unable to parse signature: " + signature, ex); - } - } - - public Signature(Signature other, ClassNameReplacer replacer) { - this.argumentTypes = Lists.newArrayList(other.argumentTypes); - for (int i = 0; i < this.argumentTypes.size(); i++) { - this.argumentTypes.set(i, new Type(this.argumentTypes.get(i), replacer)); - } - this.returnType = new Type(other.returnType, replacer); - } - - public List getArgumentTypes() { - return this.argumentTypes; - } - - public Type getReturnType() { - return this.returnType; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append("("); - for (Type type : this.argumentTypes) { - buf.append(type); - } - buf.append(")"); - buf.append(this.returnType); - return buf.toString(); - } - - public Iterable types() { - List types = Lists.newArrayList(); - types.addAll(this.argumentTypes); - types.add(this.returnType); - return types; - } - - @Override - public boolean equals(Object other) { - return other instanceof Signature && equals((Signature) other); - } - - public boolean equals(Signature other) { - return this.argumentTypes.equals(other.argumentTypes) && this.returnType.equals(other.returnType); - } - - @Override - public int hashCode() { - return Utils.combineHashesOrdered(this.argumentTypes.hashCode(), this.returnType.hashCode()); - } - - public boolean hasClass(ClassEntry classEntry) { - for (Type type : types()) { - if (type.hasClass() && type.getClassEntry().equals(classEntry)) { - return true; - } - } - return false; - } -} 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; public enum TranslationDirection { - Deobfuscating { + DEOBFUSCATING { @Override public T choose(T deobfChoice, T obfChoice) { + if (deobfChoice == null) { + return obfChoice; + } return deobfChoice; } }, - Obfuscating { + OBFUSCATING { @Override public T choose(T deobfChoice, T obfChoice) { + if (obfChoice == null) { + return deobfChoice; + } return obfChoice; } }; diff --git a/src/main/java/cuchaz/enigma/mapping/Translator.java b/src/main/java/cuchaz/enigma/mapping/Translator.java index 8d464fc..59bdf1a 100644 --- a/src/main/java/cuchaz/enigma/mapping/Translator.java +++ b/src/main/java/cuchaz/enigma/mapping/Translator.java @@ -11,332 +11,50 @@ package cuchaz.enigma.mapping; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import cuchaz.enigma.analysis.TranslationIndex; +public interface Translator { + ClassEntry getTranslatedClass(ClassEntry entry); -import java.util.List; -import java.util.Map; + ClassDefEntry getTranslatedClassDef(ClassDefEntry entry); -public class Translator { + FieldEntry getTranslatedField(FieldEntry entry); - private TranslationDirection direction; - private Map classes; - private TranslationIndex index; + FieldDefEntry getTranslatedFieldDef(FieldDefEntry entry); - private ClassNameReplacer classNameReplacer = className -> translateEntry(new ClassEntry(className)).getName(); + MethodEntry getTranslatedMethod(MethodEntry entry); - public Translator() { - this.direction = null; - this.classes = Maps.newHashMap(); - this.index = new TranslationIndex(); - } + MethodDefEntry getTranslatedMethodDef(MethodDefEntry entry); - public Translator(TranslationDirection direction, Map classes, TranslationIndex index) { - this.direction = direction; - this.classes = classes; - this.index = index; - } + LocalVariableEntry getTranslatedVariable(LocalVariableEntry entry); - public TranslationDirection getDirection() { - return direction; - } + LocalVariableDefEntry getTranslatedVariableDef(LocalVariableDefEntry entry); - public TranslationIndex getTranslationIndex() { - return index; - } + TypeDescriptor getTranslatedTypeDesc(TypeDescriptor desc); - @SuppressWarnings("unchecked") - public T translateEntry(T entry) { - if (entry instanceof ClassEntry) { - return (T) translateEntry((ClassEntry) entry); - } else if (entry instanceof FieldEntry) { - return (T) translateEntry((FieldEntry) entry); - } else if (entry instanceof MethodEntry) { - return (T) translateEntry((MethodEntry) entry); - } else if (entry instanceof ConstructorEntry) { - return (T) translateEntry((ConstructorEntry) entry); - } else if (entry instanceof ArgumentEntry) { - return (T) translateEntry((ArgumentEntry) entry); - } else if (entry instanceof LocalVariableEntry) { - return (T) translateEntry((LocalVariableEntry) entry); - } else { - throw new Error("Unknown entry type: " + entry.getClass().getName()); - } - } + MethodDescriptor getTranslatedMethodDesc(MethodDescriptor descriptor); - public String translate(T entry) { - if (entry instanceof ClassEntry) { - return translate((ClassEntry) entry); + @SuppressWarnings("unchecked") + default T getTranslatedEntry(T entry) { + if (entry instanceof ClassDefEntry) { + return (T) getTranslatedClassDef((ClassDefEntry) entry); + } else if (entry instanceof ClassEntry) { + return (T) getTranslatedClass((ClassEntry) entry); + } else if (entry instanceof FieldDefEntry) { + return (T) getTranslatedFieldDef((FieldDefEntry) entry); + } else if (entry instanceof MethodDefEntry) { + return (T) getTranslatedMethodDef((MethodDefEntry) entry); } else if (entry instanceof FieldEntry) { - return translate((FieldEntry) entry); + return (T) getTranslatedField((FieldEntry) entry); } else if (entry instanceof MethodEntry) { - return translate((MethodEntry) entry); - } else if (entry instanceof ConstructorEntry) { - return translate(entry); - } else if (entry instanceof ArgumentEntry) { - return translate((ArgumentEntry) entry); + return (T) getTranslatedMethod((MethodEntry) entry); + } else if (entry instanceof LocalVariableDefEntry) { + return (T) getTranslatedVariableDef((LocalVariableDefEntry) entry); } else if (entry instanceof LocalVariableEntry) { - return translate((LocalVariableEntry) entry); - } else { - throw new Error("Unknown entry type: " + entry.getClass().getName()); - } - } - - public String translate(LocalVariableEntry in) { - LocalVariableEntry translated = translateEntry(in); - if (translated.equals(in)) { - return null; - } - return translated.getName(); - } - - public LocalVariableEntry translateEntry(LocalVariableEntry in) { - // TODO: Implement it - return in; - } - - public String translate(ClassEntry in) { - ClassEntry translated = translateEntry(in); - if (translated.equals(in)) { - return null; - } - return translated.getName(); - } - - public String translateClass(String className) { - return translate(new ClassEntry(className)); - } - - public ClassEntry translateEntry(ClassEntry in) { - - if (in.isInnerClass()) { - - // translate as much of the class chain as we can - List mappingsChain = getClassMappingChain(in); - String[] obfClassNames = in.getName().split("\\$"); - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < obfClassNames.length; i++) { - boolean isFirstClass = buf.length() == 0; - String className = null; - ClassMapping classMapping = mappingsChain.get(i); - if (classMapping != null) { - className = this.direction.choose( - classMapping.getDeobfName(), - isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() - ); - } - if (className == null) { - className = obfClassNames[i]; - } - if (!isFirstClass) { - buf.append("$"); - } - buf.append(className); - } - return new ClassEntry(buf.toString()); - - } else { - - // normal classes are easy - ClassMapping classMapping = this.classes.get(in.getName()); - if (classMapping == null) { - return in; - } - return this.direction.choose( - classMapping.getDeobfName() != null ? new ClassEntry(classMapping.getDeobfName()) : in, - new ClassEntry(classMapping.getObfFullName()) - ); - } - } - - public String translate(FieldEntry in) { - - // resolve the class entry - ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in); - if (resolvedClassEntry != null) { - - // look for the class - ClassMapping classMapping = findClassMapping(resolvedClassEntry); - if (classMapping != null) { - - // look for the field - String translatedName = this.direction.choose( - classMapping.getDeobfFieldName(in.getName(), in.getType()), - classMapping.getObfFieldName(in.getName(), translateType(in.getType())) - ); - if (translatedName != null) { - return translatedName; - } - } - } - return null; - } - - public FieldEntry translateEntry(FieldEntry in) { - String name = translate(in); - if (name == null) { - name = in.getName(); - } - return new FieldEntry(translateEntry(in.getClassEntry()), name, translateType(in.getType())); - } - - public String translate(MethodEntry in) { - // resolve the class entry - ClassEntry resolvedClassEntry = this.index.resolveEntryClass(in, true); - if (resolvedClassEntry != null) { - - // look for class - ClassMapping classMapping = findClassMapping(resolvedClassEntry); - if (classMapping != null) { - - // look for the method - MethodMapping methodMapping = this.direction.choose( - classMapping.getMethodByObf(in.getName(), in.getSignature()), - classMapping.getMethodByDeobf(in.getName(), translateSignature(in.getSignature())) - ); - if (methodMapping != null) { - return this.direction.choose(methodMapping.getDeobfName(), methodMapping.getObfName()); - } - } - } - return null; - } - - public MethodEntry translateEntry(MethodEntry in) { - String name = translate(in); - if (name == null) { - name = in.getName(); - } - return new MethodEntry(translateEntry(in.getClassEntry()), name, translateSignature(in.getSignature())); - } - - public ConstructorEntry translateEntry(ConstructorEntry in) { - if (in.isStatic()) { - return new ConstructorEntry(translateEntry(in.getClassEntry())); - } else { - return new ConstructorEntry(translateEntry(in.getClassEntry()), translateSignature(in.getSignature())); - } - } - - public BehaviorEntry translateEntry(BehaviorEntry in) { - if (in instanceof MethodEntry) { - return translateEntry((MethodEntry) in); - } else if (in instanceof ConstructorEntry) { - return translateEntry((ConstructorEntry) in); - } - throw new Error("Wrong entry type!"); - } - - // TODO: support not identical behavior (specific to constructor) - public String translate(ArgumentEntry in) { - String classTranslate = translateArgument(in); - - // Not found in this class - if (classTranslate == null) { - List ancestry = this.index.getAncestry(in.getClassEntry()); - - // Check in mother class for the arg - for (ClassEntry entry : ancestry) { - ArgumentEntry motherArg = in.cloneToNewClass(entry); - if (this.index.entryExists(motherArg)) { - String result = translateArgument(motherArg); - if (result != null) - return result; - } - } - } - return classTranslate; - } - - public String translateArgument(ArgumentEntry in) { - // look for identical behavior in superclasses - ClassEntry entry = in.getClassEntry(); - - if (entry != null) { - // look for the class - ClassMapping classMapping = findClassMapping(entry); - if (classMapping != null) { - - // look for the method - MethodMapping methodMapping = this.direction.choose( - classMapping.getMethodByObf(in.getMethodName(), in.getMethodSignature()), - classMapping.getMethodByDeobf(in.getMethodName(), translateSignature(in.getMethodSignature())) - ); - if (methodMapping != null) { - return this.direction.choose( - methodMapping.getDeobfArgumentName(in.getIndex()), - methodMapping.getObfArgumentName(in.getIndex()) - ); - } - } - } - return null; - } - - public ArgumentEntry translateEntry(ArgumentEntry in) { - String name = translate(in); - if (name == null) { - name = in.getName(); - } - return new ArgumentEntry(translateEntry(in.getBehaviorEntry()), in.getIndex(), name); - } - - public Type translateType(Type type) { - return new Type(type, this.classNameReplacer); - } - - public Signature translateSignature(Signature signature) { - return new Signature(signature, this.classNameReplacer); - } - - private ClassMapping findClassMapping(ClassEntry in) { - List mappingChain = getClassMappingChain(in); - return mappingChain.get(mappingChain.size() - 1); - } - - private List getClassMappingChain(ClassEntry in) { - - // get a list of all the classes in the hierarchy - String[] parts = in.getName().split("\\$"); - List mappingsChain = Lists.newArrayList(); - - // get mappings for the outer class - ClassMapping outerClassMapping = this.classes.get(parts[0]); - mappingsChain.add(outerClassMapping); - - for (int i = 1; i < parts.length; i++) { - - // get mappings for the inner class - ClassMapping innerClassMapping = null; - if (outerClassMapping != null) { - innerClassMapping = this.direction.choose( - outerClassMapping.getInnerClassByObfSimple(parts[i]), - outerClassMapping.getInnerClassByDeobfThenObfSimple(parts[i]) - ); - } - mappingsChain.add(innerClassMapping); - outerClassMapping = innerClassMapping; - } - - assert (mappingsChain.size() == parts.length); - return mappingsChain; - } - - public Mappings.EntryModifier getModifier(Entry entry) { - ClassMapping classMapping = findClassMapping(entry.getClassEntry()); - if (classMapping != null && !entry.getName().equals("")) { - if (entry instanceof ClassEntry) - return classMapping.getModifier(); - else if (entry instanceof FieldEntry) { - FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType()); - return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; - } else if (entry instanceof BehaviorEntry) { - MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature()); - return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; - } else - throw new Error("Unknown entry type: " + entry.getClass().getName()); + return (T) getTranslatedVariable((LocalVariableEntry) entry); + } else if (entry instanceof TypeDescriptor) { + return (T) getTranslatedTypeDesc((TypeDescriptor) entry); + } else if (entry instanceof MethodDescriptor) { + return (T) getTranslatedMethodDesc((MethodDescriptor) entry); } - return Mappings.EntryModifier.UNCHANGED; + throw new IllegalArgumentException("Cannot translate unknown entry type"); } } diff --git a/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java new file mode 100644 index 0000000..9c0fe6d --- /dev/null +++ b/src/main/java/cuchaz/enigma/mapping/TypeDescriptor.java @@ -0,0 +1,240 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.mapping; + +import com.google.common.collect.Maps; + +import java.util.Map; +import java.util.function.Function; + +public class TypeDescriptor { + + protected final String desc; + + public TypeDescriptor(String desc) { + // don't deal with generics + // this is just for raw jvm types + if (desc.charAt(0) == 'T' || desc.indexOf('<') >= 0 || desc.indexOf('>') >= 0) { + throw new IllegalArgumentException("don't use with generic types or templates: " + desc); + } + + this.desc = desc; + } + + public static String parseFirst(String in) { + + if (in == null || in.length() <= 0) { + throw new IllegalArgumentException("No desc to parse, input is empty!"); + } + + // read one desc from the input + + char c = in.charAt(0); + + // first check for void + if (c == 'V') { + return "V"; + } + + // then check for primitives + Primitive primitive = Primitive.get(c); + if (primitive != null) { + return in.substring(0, 1); + } + + // then check for classes + if (c == 'L') { + return readClass(in); + } + + // then check for templates + if (c == 'T') { + return readClass(in); + } + + // then check for arrays + int dim = countArrayDimension(in); + if (dim > 0) { + String arrayType = TypeDescriptor.parseFirst(in.substring(dim)); + return in.substring(0, dim + arrayType.length()); + } + + throw new IllegalArgumentException("don't know how to parse: " + in); + } + + private static int countArrayDimension(String in) { + int i = 0; + while (i < in.length() && in.charAt(i) == '[') + i++; + return i; + } + + private static String readClass(String in) { + // read all the characters in the buffer until we hit a ';' + // include the parameters too + StringBuilder buf = new StringBuilder(); + int depth = 0; + for (int i = 0; i < in.length(); i++) { + char c = in.charAt(i); + buf.append(c); + + if (c == '<') { + depth++; + } else if (c == '>') { + depth--; + } else if (depth == 0 && c == ';') { + return buf.toString(); + } + } + return null; + } + + public static TypeDescriptor of(String name) { + return new TypeDescriptor("L" + name + ";"); + } + + @Override + public String toString() { + return this.desc; + } + + public boolean isVoid() { + return this.desc.length() == 1 && this.desc.charAt(0) == 'V'; + } + + public boolean isPrimitive() { + return this.desc.length() == 1 && Primitive.get(this.desc.charAt(0)) != null; + } + + public Primitive getPrimitive() { + if (!isPrimitive()) { + throw new IllegalStateException("not a primitive"); + } + return Primitive.get(this.desc.charAt(0)); + } + + public boolean isType() { + return this.desc.charAt(0) == 'L' && this.desc.charAt(this.desc.length() - 1) == ';'; + } + + public ClassEntry getOwnerEntry() { + if (isType()) { + String name = this.desc.substring(1, this.desc.length() - 1); + + int pos = name.indexOf('<'); + if (pos >= 0) { + // remove the parameters from the class name + name = name.substring(0, pos); + } + + return new ClassEntry(name); + + } else if (isArray() && getArrayType().isType()) { + return getArrayType().getOwnerEntry(); + } else { + throw new IllegalStateException("desc doesn't have a class"); + } + } + + public boolean isArray() { + return this.desc.charAt(0) == '['; + } + + public int getArrayDimension() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return countArrayDimension(this.desc); + } + + public TypeDescriptor getArrayType() { + if (!isArray()) { + throw new IllegalStateException("not an array"); + } + return new TypeDescriptor(this.desc.substring(getArrayDimension(), this.desc.length())); + } + + public boolean containsType() { + return isType() || (isArray() && getArrayType().containsType()); + } + + @Override + public boolean equals(Object other) { + return other instanceof TypeDescriptor && equals((TypeDescriptor) other); + } + + public boolean equals(TypeDescriptor other) { + return this.desc.equals(other.desc); + } + + @Override + public int hashCode() { + return this.desc.hashCode(); + } + + public TypeDescriptor remap(Function remapper) { + String desc = this.desc; + if (isType() || (isArray() && containsType())) { + String replacedName = remapper.apply(this.getOwnerEntry().getName()); + if (replacedName != null) { + if (this.isType()) { + desc = "L" + replacedName + ";"; + } else { + desc = getArrayPrefix(this.getArrayDimension()) + "L" + replacedName + ";"; + } + } + } + return new TypeDescriptor(desc); + } + + private static String getArrayPrefix(int dimension) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < dimension; i++) { + buf.append("["); + } + return buf.toString(); + } + + public enum Primitive { + Byte('B'), + Character('C'), + Short('S'), + Integer('I'), + Long('J'), + Float('F'), + Double('D'), + Boolean('Z'); + + private static final Map lookup; + + static { + lookup = Maps.newTreeMap(); + for (Primitive val : values()) { + lookup.put(val.getCode(), val); + } + } + + private char code; + + Primitive(char code) { + this.code = code; + } + + public static Primitive get(char code) { + return lookup.get(code); + } + + public char getCode() { + return this.code; + } + } +} -- cgit v1.2.3