Extended option 'dc4' to handle inlined class references (mainly Eclipse). See IDEA-135387 for an example.
This commit is contained in:
@@ -137,6 +137,8 @@ public class MethodProcessorRunnable implements Runnable {
|
||||
ExprProcessor proc = new ExprProcessor();
|
||||
proc.processStatement(root, cl);
|
||||
|
||||
SequenceHelper.condenseSequences(root);
|
||||
|
||||
while (true) {
|
||||
StackVarsProcessor stackProc = new StackVarsProcessor();
|
||||
stackProc.simplifyStackVars(root, mt, cl);
|
||||
|
||||
@@ -15,24 +15,41 @@
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.modules.decompiler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.ArrayExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.ConstExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.MonitorExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.NewExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.sforms.SSAConstructorSparseEx;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.StructClass;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.util.FastSparseSetFactory.FastSparseSet;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class SimplifyExprentsHelper {
|
||||
|
||||
static MatchEngine class14Builder = new MatchEngine();
|
||||
|
||||
private boolean firstInvocation;
|
||||
|
||||
public SimplifyExprentsHelper(boolean firstInvocation) {
|
||||
@@ -45,6 +62,8 @@ public class SimplifyExprentsHelper {
|
||||
|
||||
if (stat.getExprents() == null) {
|
||||
|
||||
boolean processClass14 = DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_CLASS_1_4);
|
||||
|
||||
while (true) {
|
||||
|
||||
boolean changed = false;
|
||||
@@ -61,6 +80,11 @@ public class SimplifyExprentsHelper {
|
||||
if (changed = buildIff(st, ssa)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// collapse inlined .class property in version 1.4 and before
|
||||
if (processClass14 && (changed = collapseInlinedClass14(st))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
res |= changed;
|
||||
@@ -852,4 +876,53 @@ public class SimplifyExprentsHelper {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static {
|
||||
class14Builder.parse(
|
||||
"statement type:if iftype:if exprsize:-1\n" +
|
||||
" exprent position:head type:if\n" +
|
||||
" exprent type:function functype:eq\n" +
|
||||
" exprent type:field name:$field$\n" +
|
||||
" exprent type:constant consttype:null\n" +
|
||||
" statement type:basicblock\n" +
|
||||
" exprent position:-1 type:assignment ret:$assignfield$\n" +
|
||||
" exprent type:var index:$var$\n" +
|
||||
" exprent type:field name:$field$\n" +
|
||||
" statement type:sequence statsize:2\n" +
|
||||
" statement type:trycatch\n" +
|
||||
" statement type:basicblock exprsize:1\n" +
|
||||
" exprent type:assignment\n" +
|
||||
" exprent type:var index:$var$\n" +
|
||||
" exprent type:invocation invclass:java/lang/Class signature:forName(Ljava/lang/String;)Ljava/lang/Class;\n" +
|
||||
" exprent position:0 type:constant consttype:string constvalue:$classname$\n" +
|
||||
" statement type:basicblock exprsize:1\n" +
|
||||
" exprent type:exit exittype:throw\n" +
|
||||
" statement type:basicblock exprsize:1\n" +
|
||||
" exprent type:assignment\n" +
|
||||
" exprent type:field name:$field$\n" +
|
||||
" exprent type:var index:$var$"
|
||||
);
|
||||
}
|
||||
|
||||
private static boolean collapseInlinedClass14(Statement stat) {
|
||||
|
||||
boolean ret = class14Builder.match(stat);
|
||||
if(ret) {
|
||||
|
||||
String class_name = (String)class14Builder.getVariableValue("$classname$");
|
||||
AssignmentExprent assfirst = (AssignmentExprent)class14Builder.getVariableValue("$assignfield$");
|
||||
|
||||
assfirst.replaceExprent(assfirst.getRight(), new ConstExprent(VarType.VARTYPE_CLASS, class_name, null));
|
||||
|
||||
List<Exprent> data = new ArrayList<Exprent>();
|
||||
data.addAll(stat.getFirst().getExprents());
|
||||
|
||||
stat.setExprents(data);
|
||||
|
||||
SequenceHelper.destroyAndFlattenStatement(stat);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,9 +23,14 @@ import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class ConstExprent extends Exprent {
|
||||
private static final Map<Integer, String> ESCAPES = new HashMap<Integer, String>() {{
|
||||
@@ -395,4 +400,37 @@ public class ConstExprent extends Exprent {
|
||||
public boolean isBoolPermitted() {
|
||||
return boolPermitted;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(!super.match(matchNode, engine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
|
||||
RuleValue rule_value = rule.getValue();
|
||||
|
||||
switch(rule.getKey()) {
|
||||
case EXPRENT_CONSTTYPE:
|
||||
if(!rule_value.value.equals(this.constType)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EXPRENT_CONSTVALUE:
|
||||
if(rule_value.isVariable()) {
|
||||
if(!engine.checkAndSetVariableValue(rule_value.value.toString(), this.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,11 +25,15 @@ import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
|
||||
import org.jetbrains.java.decompiler.struct.attr.StructExceptionsAttribute;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class ExitExprent extends Exprent {
|
||||
|
||||
@@ -151,4 +155,25 @@ public class ExitExprent extends Exprent {
|
||||
public VarType getRetType() {
|
||||
return retType;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(!super.match(matchNode, engine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer type = (Integer)matchNode.getRuleValue(MatchProperties.EXPRENT_EXITTYPE);
|
||||
if(type != null) {
|
||||
if(this.exitType != type.intValue()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.modules.decompiler.exps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
import org.jetbrains.java.decompiler.main.TextBuffer;
|
||||
import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
|
||||
@@ -22,10 +29,12 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Exprent {
|
||||
public class Exprent implements IMatchable {
|
||||
|
||||
public static final int MULTIPLE_USES = 1;
|
||||
public static final int SIDE_EFFECTS_FREE = 2;
|
||||
@@ -131,4 +140,58 @@ public class Exprent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public IMatchable findObject(MatchNode matchNode, int index) {
|
||||
|
||||
if(matchNode.getType() != MatchNode.MATCHNODE_EXPRENT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Exprent> lstAllExprents = getAllExprents();
|
||||
|
||||
if(lstAllExprents == null || lstAllExprents.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);
|
||||
if(position != null) {
|
||||
if(position.matches("-?\\d+")) {
|
||||
return lstAllExprents.get((lstAllExprents.size() + Integer.parseInt(position)) % lstAllExprents.size()); // care for negative positions
|
||||
}
|
||||
} else if(index < lstAllExprents.size()) { // use 'index' parameter
|
||||
return lstAllExprents.get(index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(matchNode.getType() != MatchNode.MATCHNODE_EXPRENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
|
||||
switch(rule.getKey()) {
|
||||
case EXPRENT_TYPE:
|
||||
if(this.type != ((Integer)rule.getValue().value).intValue()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EXPRENT_RET:
|
||||
if(!engine.checkAndSetVariableValue((String)rule.getValue().value, this)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
|
||||
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.TextUtil;
|
||||
|
||||
@@ -181,4 +185,31 @@ public class FieldExprent extends Exprent {
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(!super.match(matchNode, engine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RuleValue rule = matchNode.getRules().get(MatchProperties.EXPRENT_FIELD_NAME);
|
||||
if(rule != null) {
|
||||
if(rule.isVariable()) {
|
||||
if(!engine.checkAndSetVariableValue((String)rule.value, this.name)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(!rule.value.equals(this.name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,10 +21,15 @@ import org.jetbrains.java.decompiler.main.collectors.BytecodeMappingTracer;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.CheckTypesResult;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.ListStack;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class FunctionExprent extends Exprent {
|
||||
|
||||
@@ -604,4 +609,25 @@ public class FunctionExprent extends Exprent {
|
||||
public void setImplicitType(VarType implicitType) {
|
||||
this.implicitType = implicitType;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(!super.match(matchNode, engine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer type = (Integer)matchNode.getRuleValue(MatchProperties.EXPRENT_FUNCTYPE);
|
||||
if(type != null) {
|
||||
if(this.funcType != type.intValue()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,13 @@
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.modules.decompiler.exps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
@@ -31,12 +38,13 @@ import org.jetbrains.java.decompiler.struct.StructMethod;
|
||||
import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
|
||||
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
import org.jetbrains.java.decompiler.util.ListStack;
|
||||
import org.jetbrains.java.decompiler.util.TextUtil;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class InvocationExprent extends Exprent {
|
||||
|
||||
public static final int INVOKE_SPECIAL = 1;
|
||||
@@ -87,6 +95,7 @@ public class InvocationExprent extends Exprent {
|
||||
break;
|
||||
case CodeConstants.opc_invokedynamic:
|
||||
invocationTyp = INVOKE_DYNAMIC;
|
||||
|
||||
classname = "java/lang/Class"; // dummy class name
|
||||
invokeDynamicClassSuffix = "##Lambda_" + cn.index1 + "_" + cn.index2;
|
||||
}
|
||||
@@ -482,4 +491,47 @@ public class InvocationExprent extends Exprent {
|
||||
public String getInvokeDynamicClassSuffix() {
|
||||
return invokeDynamicClassSuffix;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(!super.match(matchNode, engine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
|
||||
RuleValue value = rule.getValue();
|
||||
|
||||
switch(rule.getKey()) {
|
||||
case EXPRENT_INVOCATION_PARAMETER:
|
||||
if(value.isVariable()) {
|
||||
if(value.parameter < lstParameters.size()) {
|
||||
if(!engine.checkAndSetVariableValue(value.value.toString(), lstParameters.get(value.parameter))) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXPRENT_INVOCATION_CLASS:
|
||||
if(!value.value.equals(this.classname)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EXPRENT_INVOCATION_SIGNATURE:
|
||||
if(!value.value.equals(this.name + this.stringDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -180,4 +184,31 @@ public class VarExprent extends Exprent {
|
||||
public void setStack(boolean stack) {
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(!super.match(matchNode, engine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RuleValue rule = matchNode.getRules().get(MatchProperties.EXPRENT_VAR_INDEX);
|
||||
if(rule != null) {
|
||||
if(rule.isVariable()) {
|
||||
if(!engine.checkAndSetVariableValue((String)rule.value, this.index)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(this.index != Integer.valueOf((String)rule.value).intValue()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,10 +22,15 @@ import org.jetbrains.java.decompiler.modules.decompiler.ExprProcessor;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.IfExprent;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
import org.jetbrains.java.decompiler.util.InterpreterUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
public class IfStatement extends Statement {
|
||||
@@ -421,4 +426,42 @@ public class IfStatement extends Statement {
|
||||
public StatEdge getElseEdge() {
|
||||
return elseedge;
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public IMatchable findObject(MatchNode matchNode, int index) {
|
||||
|
||||
IMatchable object = super.findObject(matchNode, index);
|
||||
if(object != null) {
|
||||
return object;
|
||||
}
|
||||
|
||||
if(matchNode.getType() == MatchNode.MATCHNODE_EXPRENT) {
|
||||
String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);
|
||||
if("head".equals(position)) {
|
||||
return getHeadexprent();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(!super.match(matchNode, engine)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer type = (Integer)matchNode.getRuleValue(MatchProperties.STATEMENT_IFTYPE);
|
||||
if(type != null) {
|
||||
if(this.iftype != type.intValue()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,15 @@
|
||||
*/
|
||||
package org.jetbrains.java.decompiler.modules.decompiler.stats;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.java.decompiler.code.CodeConstants;
|
||||
import org.jetbrains.java.decompiler.code.InstructionSequence;
|
||||
import org.jetbrains.java.decompiler.main.DecompilerContext;
|
||||
@@ -24,11 +33,13 @@ import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.StatEdge;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.StrongConnectivityHelper;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
|
||||
import org.jetbrains.java.decompiler.util.VBStyleCollection;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Statement {
|
||||
public class Statement implements IMatchable {
|
||||
|
||||
public static final int STATEDGE_ALL = 1 << 31;
|
||||
public static final int STATEDGE_DIRECT_ALL = 1 << 30;
|
||||
@@ -859,4 +870,78 @@ public class Statement {
|
||||
public String toString() {
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
// *****************************************************************************
|
||||
// IMatchable implementation
|
||||
// *****************************************************************************
|
||||
|
||||
public IMatchable findObject(MatchNode matchNode, int index) {
|
||||
|
||||
int node_type = matchNode.getType();
|
||||
|
||||
if(node_type == MatchNode.MATCHNODE_STATEMENT) {
|
||||
String position = (String)matchNode.getRuleValue(MatchProperties.STATEMENT_POSITION);
|
||||
if(position != null) {
|
||||
if(position.matches("-?\\d+")) {
|
||||
return this.stats.get((this.stats.size() + Integer.parseInt(position)) % this.stats.size()); // care for negative positions
|
||||
}
|
||||
} else if(index < this.stats.size()) { // use 'index' parameter
|
||||
return this.stats.get(index);
|
||||
}
|
||||
} else if(node_type == MatchNode.MATCHNODE_EXPRENT && this.exprents != null) {
|
||||
String position = (String)matchNode.getRuleValue(MatchProperties.EXPRENT_POSITION);
|
||||
if(position != null) {
|
||||
if(position.matches("-?\\d+")) {
|
||||
return this.exprents.get((this.exprents.size() + Integer.parseInt(position)) % this.exprents.size()); // care for negative positions
|
||||
}
|
||||
} else if(index < this.exprents.size()) { // use 'index' parameter
|
||||
return this.exprents.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine) {
|
||||
|
||||
if(matchNode.getType() != MatchNode.MATCHNODE_STATEMENT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for(Entry<MatchProperties, RuleValue> rule : matchNode.getRules().entrySet()) {
|
||||
switch(rule.getKey()) {
|
||||
case STATEMENT_TYPE:
|
||||
if(this.type != ((Integer)rule.getValue().value).intValue()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case STATEMENT_STATSIZE:
|
||||
if(this.stats.size() != ((Integer)rule.getValue().value).intValue()) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case STATEMENT_EXPRSIZE:
|
||||
int exprsize = ((Integer)rule.getValue().value).intValue();
|
||||
if(exprsize == -1) {
|
||||
if(this.exprents != null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if(this.exprents == null || this.exprents.size() != exprsize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATEMENT_RET:
|
||||
if(!engine.checkAndSetVariableValue((String)rule.getValue().value, this)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.jetbrains.java.decompiler.struct.match;
|
||||
|
||||
|
||||
public interface IMatchable {
|
||||
|
||||
public enum MatchProperties {
|
||||
STATEMENT_TYPE,
|
||||
STATEMENT_RET,
|
||||
STATEMENT_STATSIZE,
|
||||
STATEMENT_EXPRSIZE,
|
||||
STATEMENT_POSITION,
|
||||
STATEMENT_IFTYPE,
|
||||
|
||||
EXPRENT_TYPE,
|
||||
EXPRENT_RET,
|
||||
EXPRENT_POSITION,
|
||||
EXPRENT_FUNCTYPE,
|
||||
EXPRENT_EXITTYPE,
|
||||
EXPRENT_CONSTTYPE,
|
||||
EXPRENT_CONSTVALUE,
|
||||
EXPRENT_INVOCATION_CLASS,
|
||||
EXPRENT_INVOCATION_SIGNATURE,
|
||||
EXPRENT_INVOCATION_PARAMETER,
|
||||
EXPRENT_VAR_INDEX,
|
||||
EXPRENT_FIELD_NAME,
|
||||
}
|
||||
|
||||
public IMatchable findObject(MatchNode matchNode, int index);
|
||||
|
||||
public boolean match(MatchNode matchNode, MatchEngine engine);
|
||||
|
||||
}
|
||||
239
src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java
Normal file
239
src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java
Normal file
@@ -0,0 +1,239 @@
|
||||
package org.jetbrains.java.decompiler.struct.match;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.ExitExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.Exprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.exps.FunctionExprent;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.IfStatement;
|
||||
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
|
||||
import org.jetbrains.java.decompiler.struct.gen.VarType;
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
import org.jetbrains.java.decompiler.struct.match.MatchNode.RuleValue;
|
||||
|
||||
|
||||
public class MatchEngine {
|
||||
|
||||
private MatchNode rootNode = null;
|
||||
|
||||
private Map<String, Object> variables = new HashMap<String, Object>();
|
||||
|
||||
private static Map<String, MatchProperties> stat_properties = new HashMap<String, MatchProperties>();
|
||||
private static Map<String, MatchProperties> expr_properties = new HashMap<String, MatchProperties>();
|
||||
private static Map<String, Integer> stat_type = new HashMap<String, Integer>();
|
||||
private static Map<String, Integer> expr_type = new HashMap<String, Integer>();
|
||||
private static Map<String, Integer> expr_func_type = new HashMap<String, Integer>();
|
||||
private static Map<String, Integer> expr_exit_type = new HashMap<String, Integer>();
|
||||
private static Map<String, Integer> stat_if_type = new HashMap<String, Integer>();
|
||||
private static Map<String, VarType> expr_const_type = new HashMap<String, VarType>();
|
||||
|
||||
static {
|
||||
stat_properties.put("type", MatchProperties.STATEMENT_TYPE);
|
||||
stat_properties.put("ret", MatchProperties.STATEMENT_RET);
|
||||
stat_properties.put("position", MatchProperties.STATEMENT_POSITION);
|
||||
stat_properties.put("statsize", MatchProperties.STATEMENT_STATSIZE);
|
||||
stat_properties.put("exprsize", MatchProperties.STATEMENT_EXPRSIZE);
|
||||
stat_properties.put("iftype", MatchProperties.STATEMENT_IFTYPE);
|
||||
|
||||
expr_properties.put("type", MatchProperties.EXPRENT_TYPE);
|
||||
expr_properties.put("ret", MatchProperties.EXPRENT_RET);
|
||||
expr_properties.put("position", MatchProperties.EXPRENT_POSITION);
|
||||
expr_properties.put("functype", MatchProperties.EXPRENT_FUNCTYPE);
|
||||
expr_properties.put("exittype", MatchProperties.EXPRENT_EXITTYPE);
|
||||
expr_properties.put("consttype", MatchProperties.EXPRENT_CONSTTYPE);
|
||||
expr_properties.put("constvalue", MatchProperties.EXPRENT_CONSTVALUE);
|
||||
expr_properties.put("invclass", MatchProperties.EXPRENT_INVOCATION_CLASS);
|
||||
expr_properties.put("signature", MatchProperties.EXPRENT_INVOCATION_SIGNATURE);
|
||||
expr_properties.put("parameter", MatchProperties.EXPRENT_INVOCATION_PARAMETER);
|
||||
expr_properties.put("index", MatchProperties.EXPRENT_VAR_INDEX);
|
||||
expr_properties.put("name", MatchProperties.EXPRENT_FIELD_NAME);
|
||||
|
||||
stat_type.put("if", Statement.TYPE_IF);
|
||||
stat_type.put("do", Statement.TYPE_DO);
|
||||
stat_type.put("switch", Statement.TYPE_SWITCH);
|
||||
stat_type.put("trycatch", Statement.TYPE_TRYCATCH);
|
||||
stat_type.put("basicblock", Statement.TYPE_BASICBLOCK);
|
||||
stat_type.put("sequence", Statement.TYPE_SEQUENCE);
|
||||
|
||||
expr_type.put("array", Exprent.EXPRENT_ARRAY);
|
||||
expr_type.put("assignment", Exprent.EXPRENT_ASSIGNMENT);
|
||||
expr_type.put("constant", Exprent.EXPRENT_CONST);
|
||||
expr_type.put("exit", Exprent.EXPRENT_EXIT);
|
||||
expr_type.put("field", Exprent.EXPRENT_FIELD);
|
||||
expr_type.put("function", Exprent.EXPRENT_FUNCTION);
|
||||
expr_type.put("if", Exprent.EXPRENT_IF);
|
||||
expr_type.put("invocation", Exprent.EXPRENT_INVOCATION);
|
||||
expr_type.put("monitor", Exprent.EXPRENT_MONITOR);
|
||||
expr_type.put("new", Exprent.EXPRENT_NEW);
|
||||
expr_type.put("switch", Exprent.EXPRENT_SWITCH);
|
||||
expr_type.put("var", Exprent.EXPRENT_VAR);
|
||||
expr_type.put("annotation", Exprent.EXPRENT_ANNOTATION);
|
||||
expr_type.put("assert", Exprent.EXPRENT_ASSERT);
|
||||
|
||||
expr_func_type.put("eq", FunctionExprent.FUNCTION_EQ);
|
||||
|
||||
expr_exit_type.put("return", ExitExprent.EXIT_RETURN);
|
||||
expr_exit_type.put("throw", ExitExprent.EXIT_THROW);
|
||||
|
||||
stat_if_type.put("if", IfStatement.IFTYPE_IF);
|
||||
stat_if_type.put("ifelse", IfStatement.IFTYPE_IFELSE);
|
||||
|
||||
expr_const_type.put("null", VarType.VARTYPE_NULL);
|
||||
expr_const_type.put("string", VarType.VARTYPE_STRING);
|
||||
}
|
||||
|
||||
|
||||
public void parse(String description) {
|
||||
|
||||
// each line is a separate statement/exprent
|
||||
String[] lines = description.split("\n");
|
||||
|
||||
int depth = 0;
|
||||
LinkedList<MatchNode> stack = new LinkedList<MatchNode>();
|
||||
|
||||
for(String line : lines) {
|
||||
|
||||
List<String> properties = new ArrayList<String>(Arrays.asList(line.split("\\s+"))); // split on any number of whitespaces
|
||||
if(properties.get(0).isEmpty()) {
|
||||
properties.remove(0);
|
||||
}
|
||||
|
||||
int node_type = "statement".equals(properties.get(0)) ? MatchNode.MATCHNODE_STATEMENT : MatchNode.MATCHNODE_EXPRENT;
|
||||
|
||||
// create new node
|
||||
MatchNode matchNode = new MatchNode(node_type);
|
||||
for(int i = 1; i < properties.size(); ++i) {
|
||||
String[] values = properties.get(i).split(":");
|
||||
|
||||
MatchProperties property = (node_type == MatchNode.MATCHNODE_STATEMENT ? stat_properties : expr_properties).get(values[0]);
|
||||
if(property == null) { // unknown property defined
|
||||
throw new RuntimeException("Unknown matching property");
|
||||
} else {
|
||||
|
||||
Object value = null;
|
||||
int parameter = 0;
|
||||
|
||||
String strValue = values[1];
|
||||
if(values.length == 3) {
|
||||
parameter = Integer.parseInt(values[1]);
|
||||
strValue = values[2];
|
||||
}
|
||||
|
||||
switch(property) {
|
||||
case STATEMENT_TYPE:
|
||||
value = stat_type.get(strValue);
|
||||
break;
|
||||
case STATEMENT_STATSIZE:
|
||||
case STATEMENT_EXPRSIZE:
|
||||
value = Integer.valueOf(strValue);
|
||||
break;
|
||||
case STATEMENT_POSITION:
|
||||
case EXPRENT_POSITION:
|
||||
case EXPRENT_INVOCATION_CLASS:
|
||||
case EXPRENT_INVOCATION_SIGNATURE:
|
||||
case EXPRENT_INVOCATION_PARAMETER:
|
||||
case EXPRENT_VAR_INDEX:
|
||||
case EXPRENT_FIELD_NAME:
|
||||
case EXPRENT_CONSTVALUE:
|
||||
case STATEMENT_RET:
|
||||
case EXPRENT_RET:
|
||||
value = strValue;
|
||||
break;
|
||||
case STATEMENT_IFTYPE:
|
||||
value = stat_if_type.get(strValue);
|
||||
break;
|
||||
case EXPRENT_FUNCTYPE:
|
||||
value = expr_func_type.get(strValue);
|
||||
break;
|
||||
case EXPRENT_EXITTYPE:
|
||||
value = expr_exit_type.get(strValue);
|
||||
break;
|
||||
case EXPRENT_CONSTTYPE:
|
||||
value = expr_const_type.get(strValue);
|
||||
break;
|
||||
case EXPRENT_TYPE:
|
||||
value = expr_type.get(strValue);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Unhandled matching property");
|
||||
}
|
||||
|
||||
matchNode.addRule(property, new RuleValue(parameter, value));
|
||||
}
|
||||
}
|
||||
|
||||
if(stack.isEmpty()) { // first line, root node
|
||||
stack.push(matchNode);
|
||||
} else {
|
||||
|
||||
// return to the correct parent on the stack
|
||||
int new_depth = line.lastIndexOf(' ', depth) + 1;
|
||||
for(int i = new_depth; i <= depth; ++i) {
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
// insert new node
|
||||
stack.getFirst().addChild(matchNode);
|
||||
stack.push(matchNode);
|
||||
|
||||
depth = new_depth;
|
||||
}
|
||||
}
|
||||
|
||||
this.rootNode = stack.getLast();
|
||||
}
|
||||
|
||||
public boolean match(IMatchable object) {
|
||||
variables.clear();
|
||||
return match(this.rootNode, object);
|
||||
}
|
||||
|
||||
private boolean match(MatchNode matchNode, IMatchable object) {
|
||||
|
||||
if(!object.match(matchNode, this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int expr_index = 0;
|
||||
int stat_index = 0;
|
||||
|
||||
for(MatchNode childNode : matchNode.getChildren()) {
|
||||
boolean isStatement = childNode.getType() == MatchNode.MATCHNODE_STATEMENT;
|
||||
|
||||
IMatchable childObject = object.findObject(childNode, isStatement ? stat_index : expr_index);
|
||||
if(childObject == null || !match(childNode, childObject)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isStatement) {
|
||||
stat_index++;
|
||||
} else {
|
||||
expr_index++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkAndSetVariableValue(String name, Object value) {
|
||||
|
||||
Object old_value = variables.get(name);
|
||||
if(old_value == null) {
|
||||
variables.put(name, value);
|
||||
} else if(!old_value.equals(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object getVariableValue(String name) {
|
||||
return variables.get(name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.jetbrains.java.decompiler.struct.match;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jetbrains.java.decompiler.struct.match.IMatchable.MatchProperties;
|
||||
|
||||
public class MatchNode {
|
||||
|
||||
public static class RuleValue {
|
||||
public int parameter;
|
||||
public Object value;
|
||||
|
||||
public RuleValue(int parameter, Object value) {
|
||||
this.parameter = parameter;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public boolean isVariable() {
|
||||
String strValue = value.toString();
|
||||
return (strValue.charAt(0) == '$' && strValue.charAt(strValue.length() - 1) == '$');
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final int MATCHNODE_STATEMENT = 0;
|
||||
public static final int MATCHNODE_EXPRENT = 1;
|
||||
|
||||
private int type;
|
||||
|
||||
private Map<MatchProperties, RuleValue> rules = new HashMap<MatchProperties, RuleValue>();
|
||||
|
||||
private List<MatchNode> children = new ArrayList<MatchNode>();
|
||||
|
||||
|
||||
public MatchNode(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public void addChild(MatchNode child) {
|
||||
children.add(child);
|
||||
}
|
||||
|
||||
public void addRule(MatchProperties property, RuleValue value) {
|
||||
rules.put(property, value);
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<MatchNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public Map<MatchProperties, RuleValue> getRules() {
|
||||
return rules;
|
||||
}
|
||||
|
||||
public Object getRuleValue(MatchProperties property) {
|
||||
RuleValue rule = rules.get(property);
|
||||
return rule == null ? null : rule.value;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user