Extended option 'dc4' to handle inlined class references (mainly Eclipse). See IDEA-135387 for an example.
parent
c3b4a23fdb
commit
b3962a09ca
@ -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); |
||||||
|
|
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue