Import "Enhance switches on enums." from LexManos / MinecraftForge
This commit is contained in:
parent
d27b5cfe55
commit
2ee6d66913
@ -16,6 +16,7 @@
|
|||||||
package org.jetbrains.java.decompiler.main.rels;
|
package org.jetbrains.java.decompiler.main.rels;
|
||||||
|
|
||||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||||
|
import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
|
||||||
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
||||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||||
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||||
@ -25,8 +26,11 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
|||||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
|
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectGraph;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
|
import org.jetbrains.java.decompiler.modules.decompiler.sforms.DirectNode;
|
||||||
|
import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
|
||||||
|
import org.jetbrains.java.decompiler.modules.decompiler.stats.CatchStatement;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
|
import org.jetbrains.java.decompiler.modules.decompiler.stats.DoStatement;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
|
||||||
|
import org.jetbrains.java.decompiler.modules.decompiler.stats.SequenceStatement;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor;
|
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
|
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar;
|
||||||
@ -58,6 +62,10 @@ public class NestedClassProcessor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_ENUM)) {
|
||||||
|
gatherEnumSwitchMaps(node);
|
||||||
|
}
|
||||||
|
|
||||||
if (node.type != ClassNode.CLASS_LAMBDA) {
|
if (node.type != ClassNode.CLASS_LAMBDA) {
|
||||||
|
|
||||||
computeLocalVarsAndDefinitions(node);
|
computeLocalVarsAndDefinitions(node);
|
||||||
@ -102,6 +110,125 @@ public class NestedClassProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When Java introduced Enums they aded the ability to use them in Switch statements.
|
||||||
|
* This was done in a purely syntax sugar way using the old switch on int methods.
|
||||||
|
* The compiler creates a synthetic class with a static int array field.
|
||||||
|
* To support enums changing post compile, It initializes this field with a length of the current enum length.
|
||||||
|
* And then for every referenced enum value it adds a mapping in the form of:
|
||||||
|
* try {
|
||||||
|
* field[Enum.VALUE.ordinal()] = 1;
|
||||||
|
* } catch (FieldNotFoundException e) {}
|
||||||
|
*
|
||||||
|
* If a class has multiple switches on multiple enums, the compiler adds the init and try list to the BEGINNING of the static initalizer.
|
||||||
|
* But they add the field to the END of the fields list.
|
||||||
|
*/
|
||||||
|
private void gatherEnumSwitchMaps(ClassNode node) {
|
||||||
|
for (ClassNode child : node.nested) {
|
||||||
|
gatherEnumSwitchMaps(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodWrapper clinit = node.wrapper.getMethodWrapper("<clinit>", "()V");
|
||||||
|
if (clinit == null || clinit.root == null || clinit.root.getFirst().type != Statement.TYPE_SEQUENCE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int STATIC_FINAL_SYNTHETIC = CodeConstants.ACC_STATIC | CodeConstants.ACC_FINAL | CodeConstants.ACC_SYNTHETIC;
|
||||||
|
Set<String> potentialFields = new HashSet<String>();
|
||||||
|
for (StructField fd : node.classStruct.getFields()) {
|
||||||
|
if ((fd.getAccessFlags() & STATIC_FINAL_SYNTHETIC) == STATIC_FINAL_SYNTHETIC && "[I".equals(fd.getDescriptor())) {
|
||||||
|
potentialFields.add(fd.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (potentialFields.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SequenceStatement seq = (SequenceStatement)clinit.root.getFirst();
|
||||||
|
for (int x = 0; x < seq.getStats().size();) {
|
||||||
|
Statement stat = seq.getStats().get(x);
|
||||||
|
if (stat.type != Statement.TYPE_BASICBLOCK || stat.getExprents() == null || stat.getExprents().size() != 1 || stat.getExprents().get(0).type != Exprent.EXPRENT_ASSIGNMENT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
AssignmentExprent ass = (AssignmentExprent)stat.getExprents().get(0);
|
||||||
|
if (ass.getLeft().type != Exprent.EXPRENT_FIELD || ass.getRight().type != Exprent.EXPRENT_NEW) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FieldExprent mapField = (FieldExprent)ass.getLeft();
|
||||||
|
NewExprent _new = ((NewExprent)ass.getRight());
|
||||||
|
if (!mapField.getClassname().equals(node.classStruct.qualifiedName) || !potentialFields.contains(mapField.getName()) ||
|
||||||
|
_new.getNewtype().type != CodeConstants.TYPE_INT || _new.getNewtype().arraydim != 1 ||
|
||||||
|
_new.getLstDims().size() != 1 || _new.getLstDims().get(0).type != Exprent.EXPRENT_FUNCTION) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
FunctionExprent func = (FunctionExprent)_new.getLstDims().get(0);
|
||||||
|
if (func.getFunctype() != FunctionExprent.FUNCTION_ARRAYLENGTH || func.getLstOperands().size() != 1 || func.getLstOperands().get(0).type != Exprent.EXPRENT_INVOCATION) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
InvocationExprent invoc = (InvocationExprent)func.getLstOperands().get(0);
|
||||||
|
if (!"values".equals(invoc.getName()) || !("()[L" + invoc.getClassname() + ";").equals(invoc.getStringDescriptor())) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = mapField.getName();
|
||||||
|
String enumName = invoc.getClassname();
|
||||||
|
Map<Integer, String> idToName = new HashMap<Integer, String>();
|
||||||
|
|
||||||
|
boolean replace = false;
|
||||||
|
int y = x;
|
||||||
|
while (++y < seq.getStats().size()) {
|
||||||
|
if (seq.getStats().get(y).type != Statement.TYPE_TRYCATCH) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CatchStatement _try = (CatchStatement)seq.getStats().get(y);
|
||||||
|
Statement first = _try.getFirst();
|
||||||
|
List<Exprent> exprents = first.getExprents();
|
||||||
|
if (_try.getVars().size() != 1 || !"java/lang/NoSuchFieldError".equals(_try.getVars().get(0).getVartype().value) ||
|
||||||
|
first.type != Statement.TYPE_BASICBLOCK || exprents == null || exprents.size() != 1 || exprents.get(0).type != Exprent.EXPRENT_ASSIGNMENT) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ass = (AssignmentExprent)exprents.get(0);
|
||||||
|
if (ass.getRight().type != Exprent.EXPRENT_CONST || (!(((ConstExprent)ass.getRight()).getValue() instanceof Integer)) ||
|
||||||
|
ass.getLeft().type != Exprent.EXPRENT_ARRAY){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ArrayExprent array = (ArrayExprent)ass.getLeft();
|
||||||
|
if (array.getArray().type != Exprent.EXPRENT_FIELD || !array.getArray().equals(mapField) || array.getIndex().type != Exprent.EXPRENT_INVOCATION) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
invoc = (InvocationExprent)array.getIndex();
|
||||||
|
if (!enumName.equals(invoc.getClassname()) || !"ordinal".equals(invoc.getName()) || !"()I".equals(invoc.getStringDescriptor()) ||
|
||||||
|
invoc.getInstance().type != Exprent.EXPRENT_FIELD) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldExprent enumField = (FieldExprent)invoc.getInstance();
|
||||||
|
if (!enumName.equals(enumField.getClassname()) || !enumField.isStatic()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
idToName.put((Integer)((ConstExprent)ass.getRight()).getValue(), enumField.getName());
|
||||||
|
seq.replaceStatement(_try, getNewEmptyStatement());
|
||||||
|
replace = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (replace) {
|
||||||
|
seq.replaceStatement(seq.getStats().get(x), getNewEmptyStatement());
|
||||||
|
node.classStruct.enumSwitchMap.put(fieldName, idToName);
|
||||||
|
node.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fieldName, "[I"));
|
||||||
|
}
|
||||||
|
x = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Statement getNewEmptyStatement() {
|
||||||
|
BasicBlockStatement bstat = new BasicBlockStatement(new BasicBlock(
|
||||||
|
DecompilerContext.getCounterContainer().getCounterAndIncrement(CounterContainer.STATEMENT_COUNTER)));
|
||||||
|
bstat.setExprents(new ArrayList<Exprent>());
|
||||||
|
return bstat;
|
||||||
|
}
|
||||||
|
|
||||||
private static void setLambdaVars(ClassNode parent, ClassNode child) {
|
private static void setLambdaVars(ClassNode parent, ClassNode child) {
|
||||||
|
|
||||||
if (child.lambda_information.is_method_reference) { // method reference, no code and no parameters
|
if (child.lambda_information.is_method_reference) { // method reference, no code and no parameters
|
||||||
|
@ -24,6 +24,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.DecHelper;
|
|||||||
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
|
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
|
||||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
||||||
|
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||||
import org.jetbrains.java.decompiler.util.Util;
|
import org.jetbrains.java.decompiler.util.Util;
|
||||||
@ -137,7 +138,12 @@ public class SwitchStatement extends Statement {
|
|||||||
tracer.incrementCurrentSourceLine();
|
tracer.incrementCurrentSourceLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.append(indstr).append(headexprent.get(0).toJava(indent, tracer)).append(" {").append(new_line_separator);
|
// Doesn't seem to be a better place to put it so enhance things here
|
||||||
|
Map<Integer, String> remaps = enhanceHead(headexprent.get(0), buf, indent, tracer);
|
||||||
|
|
||||||
|
if (remaps == null) {
|
||||||
|
buf.append(indstr).append(headexprent.get(0).toJava(indent, tracer)).append(" {").append(new_line_separator);
|
||||||
|
}
|
||||||
tracer.incrementCurrentSourceLine();
|
tracer.incrementCurrentSourceLine();
|
||||||
|
|
||||||
VarType switch_type = headexprent.get(0).getExprType();
|
VarType switch_type = headexprent.get(0).getExprType();
|
||||||
@ -157,7 +163,13 @@ public class SwitchStatement extends Statement {
|
|||||||
ConstExprent value = (ConstExprent)values.get(j).copy();
|
ConstExprent value = (ConstExprent)values.get(j).copy();
|
||||||
value.setConsttype(switch_type);
|
value.setConsttype(switch_type);
|
||||||
|
|
||||||
buf.append(indstr).append("case ").append(value.toJava(indent, tracer)).append(":").append(new_line_separator);
|
buf.append(indstr).append("case ");
|
||||||
|
if (remaps == null) {
|
||||||
|
buf.append(value.toJava(indent, tracer));
|
||||||
|
} else {
|
||||||
|
buf.append(remaps.get(value.getValue()));
|
||||||
|
}
|
||||||
|
buf.append(":").append(new_line_separator);
|
||||||
tracer.incrementCurrentSourceLine();
|
tracer.incrementCurrentSourceLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,6 +188,44 @@ public class SwitchStatement extends Statement {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<Integer, String> enhanceHead(Exprent exprent, StringBuilder buf, int indent, BytecodeMappingTracer tracer) {
|
||||||
|
if (exprent.type != Exprent.EXPRENT_SWITCH) return null;
|
||||||
|
|
||||||
|
SwitchExprent swtch = (SwitchExprent)exprent;
|
||||||
|
if (swtch.getValue().type != Exprent.EXPRENT_ARRAY) return null;
|
||||||
|
|
||||||
|
ArrayExprent array = (ArrayExprent)swtch.getValue();
|
||||||
|
if (array.getArray().type != Exprent.EXPRENT_FIELD || array.getIndex().type != Exprent.EXPRENT_INVOCATION) return null;
|
||||||
|
|
||||||
|
FieldExprent field = (FieldExprent)array.getArray();
|
||||||
|
InvocationExprent invoc = (InvocationExprent)array.getIndex();
|
||||||
|
StructClass cls = DecompilerContext.getStructContext().getClass(field.getClassname());
|
||||||
|
if (cls == null || !field.isStatic() || !"ordinal".equals(invoc.getName()) || !"()I".equals(invoc.getStringDescriptor())) return null;
|
||||||
|
|
||||||
|
Map<Integer, String> ret = cls.enumSwitchMap.get(field.getName());
|
||||||
|
if (ret == null) return null;
|
||||||
|
|
||||||
|
for (List<ConstExprent> lst : getCaseValues()) {
|
||||||
|
if (lst != null) {
|
||||||
|
for (ConstExprent cst : lst) {
|
||||||
|
if (cst != null && (!(cst.getValue() instanceof Integer) || !ret.containsKey(cst.getValue()))) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer.addMapping(swtch.bytecode);
|
||||||
|
tracer.addMapping(field.bytecode);
|
||||||
|
tracer.addMapping(invoc.bytecode);
|
||||||
|
|
||||||
|
String indstr = InterpreterUtil.getIndentString(indent);
|
||||||
|
String new_line_separator = DecompilerContext.getNewLineSeparator();
|
||||||
|
|
||||||
|
buf.append(indstr).append("switch (").append((invoc.getInstance().toJava(indent, tracer))).append(") {").append(new_line_separator);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
public void initExprents() {
|
public void initExprents() {
|
||||||
SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1);
|
SwitchExprent swexpr = (SwitchExprent)first.getExprents().remove(first.getExprents().size() - 1);
|
||||||
swexpr.setCaseValues(caseValues);
|
swexpr.setCaseValues(caseValues);
|
||||||
|
@ -24,6 +24,8 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
|||||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
class_file {
|
class_file {
|
||||||
@ -58,6 +60,7 @@ public class StructClass extends StructMember {
|
|||||||
private final String[] interfaceNames;
|
private final String[] interfaceNames;
|
||||||
private final VBStyleCollection<StructField, String> fields;
|
private final VBStyleCollection<StructField, String> fields;
|
||||||
private final VBStyleCollection<StructMethod, String> methods;
|
private final VBStyleCollection<StructMethod, String> methods;
|
||||||
|
public final Map<String, Map<Integer, String>> enumSwitchMap = new HashMap<String, Map<Integer, String>>();
|
||||||
|
|
||||||
private ConstantPool pool;
|
private ConstantPool pool;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user