summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java449
1 files changed, 449 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java
new file mode 100644
index 000000000000..7e5eaa54326a
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedMemberAccess.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.java.decompiler.main.rels;
+
+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.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.*;
+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;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+
+public class NestedMemberAccess {
+
+ private static final int METHOD_ACCESS_NORMAL = 1;
+ private static final int METHOD_ACCESS_FIELD_GET = 2;
+ private static final int METHOD_ACCESS_FIELD_SET = 3;
+ private static final int METHOD_ACCESS_METHOD = 4;
+
+ private boolean noSynthFlag;
+ private Map<MethodWrapper, Integer> mapMethodType = new HashMap<MethodWrapper, Integer>();
+
+
+ public void propagateMemberAccess(ClassNode root) {
+ if (root.nested.isEmpty()) {
+ return;
+ }
+
+ noSynthFlag = 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 method : node.wrapper.getMethods()) {
+ computeMethodType(node, method);
+ }
+ }
+
+ private void computeMethodType(ClassNode node, MethodWrapper method) {
+ int type = METHOD_ACCESS_NORMAL;
+
+ if (method.root != null) {
+ DirectGraph graph = method.getOrBuildGraph();
+
+ StructMethod mt = method.methodStruct;
+ if ((noSynthFlag || mt.isSynthetic()) && mt.hasModifier(CodeConstants.ACC_STATIC)) {
+ 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(mt.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_FIELD_GET;
+ }
+ }
+ }
+ break;
+ case Exprent.EXPRENT_VAR: // qualified this
+ if (parcount == 1) {
+ // this or final variable
+ if (((VarExprent)exprCore).getIndex() != 0) {
+ type = METHOD_ACCESS_FIELD_GET;
+ }
+ }
+
+ 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_FIELD_SET;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ 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(mt.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_FIELD_SET;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (type != METHOD_ACCESS_NORMAL) {
+ mapMethodType.put(method, type);
+ }
+ else {
+ mapMethodType.remove(method);
+ }
+ }
+
+
+ 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);
+ }
+
+ while (true) {
+
+ 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 static 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_FIELD_GET:
+ 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_FIELD_SET:
+ AssignmentExprent ret;
+ if (source.type == Exprent.EXPRENT_EXIT) {
+ ExitExprent extex = (ExitExprent)source;
+ ret = (AssignmentExprent)extex.getValue().copy();
+ }
+ else {
+ ret = (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.isSynthetic()) {
+ hide = false;
+ }
+ }
+ if (hide) {
+ node.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(invexpr.getName(), invexpr.getStringDescriptor()));
+ }
+ }
+
+ return retexprent;
+ }
+}