From b3962a09cafd76a6b65b6e1877451aae845cda8e Mon Sep 17 00:00:00 2001 From: Stiver Date: Sun, 1 Mar 2015 13:51:30 +0100 Subject: [PATCH] Extended option 'dc4' to handle inlined class references (mainly Eclipse). See IDEA-135387 for an example. --- .../main/rels/MethodProcessorRunnable.java | 2 + .../decompiler/SimplifyExprentsHelper.java | 81 +++++- .../modules/decompiler/exps/ConstExprent.java | 38 +++ .../modules/decompiler/exps/ExitExprent.java | 25 ++ .../modules/decompiler/exps/Exprent.java | 69 ++++- .../modules/decompiler/exps/FieldExprent.java | 31 +++ .../decompiler/exps/FunctionExprent.java | 26 ++ .../decompiler/exps/InvocationExprent.java | 56 +++- .../modules/decompiler/exps/VarExprent.java | 31 +++ .../modules/decompiler/stats/IfStatement.java | 43 ++++ .../modules/decompiler/stats/Statement.java | 91 ++++++- .../decompiler/struct/match/IMatchable.java | 32 +++ .../decompiler/struct/match/MatchEngine.java | 239 ++++++++++++++++++ .../decompiler/struct/match/MatchNode.java | 70 +++++ 14 files changed, 822 insertions(+), 12 deletions(-) create mode 100644 src/org/jetbrains/java/decompiler/struct/match/IMatchable.java create mode 100644 src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java create mode 100644 src/org/jetbrains/java/decompiler/struct/match/MatchNode.java diff --git a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java index f3fba85..1c90fb6 100644 --- a/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java +++ b/src/org/jetbrains/java/decompiler/main/rels/MethodProcessorRunnable.java @@ -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); diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java index dffb657..26e9ceb 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/SimplifyExprentsHelper.java @@ -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 data = new ArrayList(); + data.addAll(stat.getFirst().getExprents()); + + stat.setExprents(data); + + SequenceHelper.destroyAndFlattenStatement(stat); + } + + return ret; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java index 2491848..4b9b687 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ConstExprent.java @@ -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 ESCAPES = new HashMap() {{ @@ -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 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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java index 6da27eb..f0aa7d2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/ExitExprent.java @@ -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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java index 82be5ca..fbd146e 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/Exprent.java @@ -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 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 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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java index bfdd8db..4ac8123 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java @@ -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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java index 8cb02b8..92ae381 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/FunctionExprent.java @@ -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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java index 717e060..294a115 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/InvocationExprent.java @@ -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 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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java index 26cec62..4436cb5 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java @@ -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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java index a03e70a..308435a 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/IfStatement.java @@ -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; + } + } diff --git a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java index af7cb46..885b5e2 100644 --- a/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java +++ b/src/org/jetbrains/java/decompiler/modules/decompiler/stats/Statement.java @@ -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 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; + } + } diff --git a/src/org/jetbrains/java/decompiler/struct/match/IMatchable.java b/src/org/jetbrains/java/decompiler/struct/match/IMatchable.java new file mode 100644 index 0000000..8ab89e6 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/match/IMatchable.java @@ -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); + +} diff --git a/src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java b/src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java new file mode 100644 index 0000000..56bc2bf --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/match/MatchEngine.java @@ -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 variables = new HashMap(); + + private static Map stat_properties = new HashMap(); + private static Map expr_properties = new HashMap(); + private static Map stat_type = new HashMap(); + private static Map expr_type = new HashMap(); + private static Map expr_func_type = new HashMap(); + private static Map expr_exit_type = new HashMap(); + private static Map stat_if_type = new HashMap(); + private static Map expr_const_type = new HashMap(); + + 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 stack = new LinkedList(); + + for(String line : lines) { + + List properties = new ArrayList(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); + } + +} diff --git a/src/org/jetbrains/java/decompiler/struct/match/MatchNode.java b/src/org/jetbrains/java/decompiler/struct/match/MatchNode.java new file mode 100644 index 0000000..e598820 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/struct/match/MatchNode.java @@ -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 rules = new HashMap(); + + private List children = new ArrayList(); + + + 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 getChildren() { + return children; + } + + public Map getRules() { + return rules; + } + + public Object getRuleValue(MatchProperties property) { + RuleValue rule = rules.get(property); + return rule == null ? null : rule.value; + } + +}