java-decompiler: post-import cleanup (classes moved)
This commit is contained in:
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user