/*******************************************************************************
* 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 com.google.common.collect.Maps;
import cuchaz.enigma.mapping.entry.ClassEntry;
import cuchaz.enigma.mapping.entry.MethodEntry;
import cuchaz.enigma.throwables.IllegalNameException;
import cuchaz.enigma.throwables.MappingConflict;
import java.util.Map;
public class MethodMapping implements Comparable, MemberMapping {
private String obfName;
private String deobfName;
private MethodDescriptor obfDescriptor;
private Map localVariables;
private Mappings.EntryModifier modifier;
public MethodMapping(String obfName, MethodDescriptor obfDescriptor) {
this(obfName, obfDescriptor, null, Mappings.EntryModifier.UNCHANGED);
}
public MethodMapping(String obfName, MethodDescriptor obfDescriptor, String deobfName) {
this(obfName, obfDescriptor, deobfName, Mappings.EntryModifier.UNCHANGED);
}
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.obfDescriptor = obfDescriptor;
this.localVariables = Maps.newTreeMap();
this.modifier = modifier;
}
public MethodMapping(MethodMapping other, Translator translator) {
this.obfName = other.obfName;
this.deobfName = other.deobfName;
this.modifier = other.modifier;
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()));
}
}
@Override
public String getObfName() {
return this.obfName;
}
public void setObfName(String name) {
try {
NameValidator.validateMethodName(name);
} catch (IllegalNameException ex) {
// Invalid name, damn obfuscation! Map to a deob name with another name to avoid issues
if (this.deobfName == null) {
System.err.println("WARNING: " + name + " is conflicting, auto deobfuscate to " + (name + "_auto_deob"));
setDeobfName(name + "_auto_deob");
}
}
this.obfName = name;
}
public String getDeobfName() {
return this.deobfName;
}
public void setDeobfName(String val) {
this.deobfName = NameValidator.validateMethodName(val);
}
public MethodDescriptor getObfDesc() {
return this.obfDescriptor;
}
public void setObfDescriptor(MethodDescriptor val) {
this.obfDescriptor = val;
}
public Iterable arguments() {
return this.localVariables.values();
}
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.localVariables.put(localVariableMapping.getIndex(), localVariableMapping);
}
public String getObfLocalVariableName(int index) {
LocalVariableMapping localVariableMapping = this.localVariables.get(index);
if (localVariableMapping != null) {
return localVariableMapping.getName();
}
return null;
}
public String getDeobfLocalVariableName(int index) {
LocalVariableMapping localVariableMapping = this.localVariables.get(index);
if (localVariableMapping != null) {
return localVariableMapping.getName();
}
return 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 {
localVariableMapping.setName(name);
}
}
public void removeLocalVariableName(int index) {
boolean wasRemoved = this.localVariables.remove(index) != null;
assert (wasRemoved);
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("\t");
buf.append(this.obfName);
buf.append(" <-> ");
buf.append(this.deobfName);
buf.append("\n");
buf.append("\t");
buf.append(this.obfDescriptor);
buf.append("\n");
buf.append("\tLocal Variables:\n");
for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
buf.append("\t\t");
buf.append(localVariableMapping.getIndex());
buf.append(" -> ");
buf.append(localVariableMapping.getName());
buf.append("\n");
}
return buf.toString();
}
@Override
public int compareTo(MethodMapping other) {
return (this.obfName + this.obfDescriptor).compareTo(other.obfName + other.obfDescriptor);
}
public boolean containsLocalVariable(String name) {
for (LocalVariableMapping localVariableMapping : this.localVariables.values()) {
if (localVariableMapping.getName().equals(name)) {
return true;
}
}
return false;
}
public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) {
// rename obf classes in the signature
MethodDescriptor newDescriptor = obfDescriptor.remap(className -> {
if (className.equals(oldObfClassName)) {
return newObfClassName;
}
return className;
});
if (!newDescriptor.equals(this.obfDescriptor)) {
this.obfDescriptor = newDescriptor;
return true;
}
return false;
}
@Override
public MethodEntry getObfEntry(ClassEntry classEntry) {
return new MethodEntry(classEntry, this.obfName, this.obfDescriptor);
}
public Mappings.EntryModifier getModifier() {
return modifier;
}
public void setModifier(Mappings.EntryModifier modifier) {
this.modifier = modifier;
}
}