Fork of the Fernflower decompiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fernflower/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java

458 lines
14 KiB

11 years ago
/*
* Fernflower - The Analytical Java Decompiler
* http://www.reversed-java.com
*
* (C) 2008 - 2010, Stiver
*
* This software is NEITHER public domain NOR free software
* as per GNU License. See license.txt for more details.
*
* This software is distributed WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE.
*/
package org.jetbrains.java.decompiler.main.rels;
11 years ago
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.collectors.CounterContainer;
import org.jetbrains.java.decompiler.main.collectors.VarNamesCollector;
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences;
import org.jetbrains.java.decompiler.modules.decompiler.exps.AssignmentExprent;
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.FieldExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.InvocationExprent;
import org.jetbrains.java.decompiler.modules.decompiler.exps.VarExprent;
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.vars.VarVersionPaar;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.util.InterpreterUtil;
11 years ago
public class NestedMemberAccess {
private static final int METHOD_ACCESS_NORMAL = 1;
private static final int METHOD_ACCESS_FIELDGET = 2;
private static final int METHOD_ACCESS_FIELDSET = 3;
private static final int METHOD_ACCESS_METHOD = 4;
private boolean notSetSync;
private HashMap<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>();
public void propagateMemberAccess(ClassNode root) {
if(root.nested.isEmpty()) {
return;
}
notSetSync = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET);
computeMethodTypes(root);
eliminateStaticAccess(root);
}
private void computeMethodTypes(ClassNode node) {
if(node.type == ClassNode.CLASS_LAMBDA) {
return;
}
11 years ago
for(ClassNode nd : node.nested) {
computeMethodTypes(nd);
}
for(MethodWrapper meth : node.wrapper.getMethods()) {
computeMethodType(node, meth);
}
}
private void computeMethodType(ClassNode node, MethodWrapper meth) {
int type = METHOD_ACCESS_NORMAL;
if(meth.root != null) {
DirectGraph graph = meth.getOrBuildGraph();
int flags = meth.methodStruct.getAccessFlags();
if(((flags & CodeConstants.ACC_SYNTHETIC) != 0 || meth.methodStruct.getAttributes().containsKey("Synthetic") || notSetSync) &&
(flags & CodeConstants.ACC_STATIC) != 0) {
if(graph.nodes.size() == 2) { // incl. dummy exit node
if(graph.first.exprents.size() == 1) {
Exprent exprent = graph.first.exprents.get(0);
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
Exprent exprCore = exprent;
if(exprent.type == Exprent.EXPRENT_EXIT) {
ExitExprent exexpr = (ExitExprent)exprent;
if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
exprCore = exexpr.getValue();
}
}
switch(exprCore.type) {
case Exprent.EXPRENT_FIELD:
FieldExprent fexpr = (FieldExprent)exprCore;
if((parcount == 1 && !fexpr.isStatic()) ||
(parcount == 0 && fexpr.isStatic())) {
if(fexpr.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpr.isStatic() || (fexpr.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpr.getInstance()).getIndex() == 0)) {
type = METHOD_ACCESS_FIELDGET;
}
}
}
break;
case Exprent.EXPRENT_VAR: // qualified this
if(parcount == 1) {
// this or final variable
if(((VarExprent)exprCore).getIndex() != 0) {
type = METHOD_ACCESS_FIELDGET;
}
}
break;
case Exprent.EXPRENT_INVOCATION:
type = METHOD_ACCESS_METHOD;
break;
case Exprent.EXPRENT_ASSIGNMENT:
AssignmentExprent asexpr = (AssignmentExprent)exprCore;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
if(type == METHOD_ACCESS_METHOD) { // FIXME: check for private flag of the method
type = METHOD_ACCESS_NORMAL;
InvocationExprent invexpr = (InvocationExprent)exprCore;
if((invexpr.isStatic() && invexpr.getLstParameters().size() == parcount) || (!invexpr.isStatic() && invexpr.getInstance().type == Exprent.EXPRENT_VAR
&& ((VarExprent)invexpr.getInstance()).getIndex() == 0 && invexpr.getLstParameters().size() == parcount-1)) {
boolean equalpars = true;
for(int i=0;i<invexpr.getLstParameters().size();i++) {
Exprent parexpr = invexpr.getLstParameters().get(i);
if(parexpr.type != Exprent.EXPRENT_VAR ||
((VarExprent)parexpr).getIndex() != i + (invexpr.isStatic()?0:1)) {
equalpars = false;
break;
}
}
if(equalpars) {
type = METHOD_ACCESS_METHOD;
}
}
}
} else if(graph.first.exprents.size() == 2) {
Exprent exprentFirst = graph.first.exprents.get(0);
Exprent exprentSecond = graph.first.exprents.get(1);
if(exprentFirst.type == Exprent.EXPRENT_ASSIGNMENT &&
exprentSecond.type == Exprent.EXPRENT_EXIT) {
MethodDescriptor mtdesc = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
int parcount = mtdesc.params.length;
AssignmentExprent asexpr = (AssignmentExprent)exprentFirst;
if(asexpr.getLeft().type == Exprent.EXPRENT_FIELD && asexpr.getRight().type == Exprent.EXPRENT_VAR) {
FieldExprent fexpras = (FieldExprent)asexpr.getLeft();
if((parcount == 2 && !fexpras.isStatic()) ||
(parcount == 1 && fexpras.isStatic())) {
if(fexpras.getClassname().equals(node.classStruct.qualifiedName)) { // FIXME: check for private flag of the field
if(fexpras.isStatic() || (fexpras.getInstance().type == Exprent.EXPRENT_VAR && ((VarExprent)fexpras.getInstance()).getIndex() == 0)) {
if(((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
ExitExprent exexpr = (ExitExprent)exprentSecond;
if(exexpr.getExittype() == ExitExprent.EXIT_RETURN && exexpr.getValue() != null) {
if(exexpr.getValue().type == Exprent.EXPRENT_VAR &&
((VarExprent)asexpr.getRight()).getIndex() == parcount - 1) {
type = METHOD_ACCESS_FIELDSET;
}
}
}
}
}
}
}
}
}
}
}
}
if(type != METHOD_ACCESS_NORMAL) {
mapMethodType.put(meth, type);
} else {
mapMethodType.remove(meth);
}
}
private void eliminateStaticAccess(ClassNode node) {
if(node.type == ClassNode.CLASS_LAMBDA) {
return;
}
11 years ago
for(MethodWrapper meth : node.wrapper.getMethods()) {
if(meth.root != null) {
boolean replaced = false;
DirectGraph graph = meth.getOrBuildGraph();
HashSet<DirectNode> setVisited = new HashSet<DirectNode>();
LinkedList<DirectNode> stack = new LinkedList<DirectNode>();
stack.add(graph.first);
while(!stack.isEmpty()) { // TODO: replace with interface iterator?
DirectNode nd = stack.removeFirst();
if(setVisited.contains(nd)) {
continue;
}
setVisited.add(nd);
for(int i=0;i<nd.exprents.size();i++) {
Exprent exprent = nd.exprents.get(i);
replaced |= replaceInvocations(node, meth, exprent);
if(exprent.type == Exprent.EXPRENT_INVOCATION) {
Exprent ret = replaceAccessExprent(node, meth, (InvocationExprent)exprent);
if(ret != null) {
nd.exprents.set(i, ret);
replaced = true;
}
}
}
for(DirectNode ndx: nd.succs) {
stack.add(ndx);
}
}
if(replaced) {
computeMethodType(node, meth);
}
}
}
for(ClassNode child : node.nested) {
eliminateStaticAccess(child);
}
}
private boolean replaceInvocations(ClassNode caller, MethodWrapper meth, Exprent exprent) {
boolean res = false;
for(Exprent expr : exprent.getAllExprents()) {
res |= replaceInvocations(caller, meth, expr);
}
for(;;) {
boolean found = false;
for(Exprent expr : exprent.getAllExprents()) {
if(expr.type == Exprent.EXPRENT_INVOCATION) {
Exprent newexpr = replaceAccessExprent(caller, meth, (InvocationExprent)expr);
if(newexpr != null) {
exprent.replaceExprent(expr, newexpr);
found = true;
res = true;
break;
}
}
}
if(!found) {
break;
}
}
return res;
}
private boolean sameTree(ClassNode caller, ClassNode callee) {
if(caller.classStruct.qualifiedName.equals(callee.classStruct.qualifiedName)) {
return false;
}
while(caller.parent != null) {
caller = caller.parent;
}
while(callee.parent != null) {
callee = callee.parent;
}
return caller == callee;
}
private Exprent replaceAccessExprent(ClassNode caller, MethodWrapper methdest, InvocationExprent invexpr) {
ClassNode node = DecompilerContext.getClassprocessor().getMapRootClasses().get(invexpr.getClassname());
MethodWrapper methsource = null;
if(node != null && node.wrapper != null) {
methsource = node.wrapper.getMethodWrapper(invexpr.getName(), invexpr.getStringDescriptor());
}
if(methsource == null || !mapMethodType.containsKey(methsource)) {
return null;
}
// if same method, return
if(node.classStruct.qualifiedName.equals(caller.classStruct.qualifiedName) &&
methsource.methodStruct.getName().equals(methdest.methodStruct.getName()) &&
methsource.methodStruct.getDescriptor().equals(methdest.methodStruct.getDescriptor())) {
// no recursive invocations permitted!
return null;
}
int type = mapMethodType.get(methsource);
// // FIXME: impossible case. METHOD_ACCESS_NORMAL is not saved in the map
// if(type == METHOD_ACCESS_NORMAL) {
// return null;
// }
if(!sameTree(caller, node)) {
return null;
}
DirectGraph graph = methsource.getOrBuildGraph();
Exprent source = graph.first.exprents.get(0);
Exprent retexprent = null;
switch(type) {
case METHOD_ACCESS_FIELDGET:
ExitExprent exsource = (ExitExprent)source;
if(exsource.getValue().type == Exprent.EXPRENT_VAR) { // qualified this
VarExprent var = (VarExprent)exsource.getValue();
String varname = methsource.varproc.getVarName(new VarVersionPaar(var));
if(!methdest.setOuterVarNames.contains(varname)) {
VarNamesCollector vnc = new VarNamesCollector();
vnc.addName(varname);
methdest.varproc.refreshVarNames(vnc);
methdest.setOuterVarNames.add(varname);
}
int index = methdest.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER);
VarExprent ret = new VarExprent(index, var.getVartype(), methdest.varproc);
methdest.varproc.setVarName(new VarVersionPaar(index, 0), varname);
retexprent = ret;
} else { // field
FieldExprent ret = (FieldExprent)exsource.getValue().copy();
if(!ret.isStatic()) {
ret.replaceExprent(ret.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
}
break;
case METHOD_ACCESS_FIELDSET:
AssignmentExprent ret;
if(source.type == Exprent.EXPRENT_EXIT) {
ExitExprent extex = (ExitExprent)source;
ret = (AssignmentExprent)((AssignmentExprent)extex.getValue()).copy();
} else {
ret = (AssignmentExprent)((AssignmentExprent)source).copy();
}
FieldExprent fexpr = (FieldExprent)ret.getLeft();
if(fexpr.isStatic()) {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(0));
} else {
ret.replaceExprent(ret.getRight(), invexpr.getLstParameters().get(1));
fexpr.replaceExprent(fexpr.getInstance(), invexpr.getLstParameters().get(0));
}
retexprent = ret;
break;
case METHOD_ACCESS_METHOD:
if(source.type == Exprent.EXPRENT_EXIT) {
source = ((ExitExprent)source).getValue();
}
InvocationExprent invret = (InvocationExprent)source.copy();
int index = 0;
if(!invret.isStatic()) {
invret.replaceExprent(invret.getInstance(), invexpr.getLstParameters().get(0));
index = 1;
}
for(int i=0;i<invret.getLstParameters().size();i++) {
invret.replaceExprent(invret.getLstParameters().get(i), invexpr.getLstParameters().get(i + index));
}
retexprent = invret;
}
if(retexprent != null) {
// hide synthetic access method
boolean hide = true;
if(node.type == ClassNode.CLASS_ROOT || (node.access & CodeConstants.ACC_STATIC) != 0) {
StructMethod mt = methsource.methodStruct;
if((mt.getAccessFlags() & CodeConstants.ACC_SYNTHETIC) == 0 && !mt.getAttributes().containsKey("Synthetic")) {
hide = false;
}
}
if(hide) {
node.wrapper.getHideMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
}
}
return retexprent;
}
}