diff options
Diffstat (limited to 'src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java')
-rw-r--r-- | src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java new file mode 100644 index 0000000..3eda632 --- /dev/null +++ b/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java @@ -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; + } + + +} |