diff options
author | Tor Norbye <tnorbye@google.com> | 2014-09-18 11:43:07 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2014-09-18 11:43:28 -0700 |
commit | e782c57d74000722f9db4c9426317410520670c6 (patch) | |
tree | 6e5d3e8934107ffabb7661f8bfc0e1a08eb37faf /plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java | |
parent | c3d3a90f6b4ead083d63e28e6b9fcea93d675678 (diff) | |
download | idea-e782c57d74000722f9db4c9426317410520670c6.tar.gz |
Snapshot idea/138.2210 from git://git.jetbrains.org/idea/community.git
Change-Id: I8f0204d7887ee78cf1fd8c09f936c5afff0edd2f
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java')
-rw-r--r-- | plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java | 1030 |
1 files changed, 1030 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java new file mode 100644 index 000000000000..2e839461125c --- /dev/null +++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java @@ -0,0 +1,1030 @@ +/* + * 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.IFernflowerLogger; +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.stats.DoStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement; +import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarTypeProcessor; +import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPaar; +import org.jetbrains.java.decompiler.struct.StructClass; +import org.jetbrains.java.decompiler.struct.StructField; +import org.jetbrains.java.decompiler.struct.StructMethod; +import org.jetbrains.java.decompiler.struct.attr.StructEnclosingMethodAttribute; +import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor; +import org.jetbrains.java.decompiler.struct.gen.VarType; +import org.jetbrains.java.decompiler.util.InterpreterUtil; + +import java.util.*; +import java.util.Map.Entry; + +public class NestedClassProcessor { + + + public void processClass(ClassNode root, ClassNode node) { + + // hide synthetic lambda content methods + if (node.type == ClassNode.CLASS_LAMBDA && !node.lambda_information.is_method_reference) { + ClassNode node_content = DecompilerContext.getClassProcessor().getMapRootClasses().get(node.classStruct.qualifiedName); + if (node_content != null && node_content.wrapper != null) { + node_content.wrapper.getHiddenMembers().add(node.lambda_information.content_method_key); + } + } + + if (node.nested.isEmpty()) { + return; + } + + if (node.type != ClassNode.CLASS_LAMBDA) { + + computeLocalVarsAndDefinitions(node); + + // for each local or anonymous class ensure not empty enclosing method + checkNotFoundClasses(root, node); + } + + int nameless = 0, synthetics = 0; + for (ClassNode child : node.nested) { + // ensure not-empty class name + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) { + StructClass cl = child.classStruct; + if ((child.access & CodeConstants.ACC_SYNTHETIC) != 0 || cl.isSynthetic()) { + child.simpleName = "SyntheticClass_" + (++synthetics); + } + else { + DecompilerContext.getLogger().writeMessage("Nameless local or member class " + cl.qualifiedName + "!", + IFernflowerLogger.Severity.WARN); + child.simpleName = "NamelessClass_" + (++nameless); + } + } + } + + for (ClassNode child : node.nested) { + if (child.type == ClassNode.CLASS_LAMBDA) { + setLambdaVars(node, child); + } + else { + if (child.type != ClassNode.CLASS_MEMBER || (child.access & CodeConstants.ACC_STATIC) == 0) { + insertLocalVars(node, child); + + if (child.type == ClassNode.CLASS_LOCAL) { + setLocalClassDefinition(node.wrapper.getMethods().getWithKey(child.enclosingMethod), child); + } + } + } + } + + for (ClassNode child : node.nested) { + processClass(root, child); + } + } + + private static void setLambdaVars(ClassNode parent, ClassNode child) { + + if (child.lambda_information.is_method_reference) { // method reference, no code and no parameters + return; + } + + final MethodWrapper meth = parent.wrapper.getMethods().getWithKey(child.lambda_information.content_method_key); + final MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + + MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambda_information.method_descriptor); + final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambda_information.content_method_descriptor); + + final int vars_count = md_content.params.length - md_lambda.params.length; + // if(vars_count < 0) { // should not happen, but just in case... + // vars_count = 0; + // } + + final boolean is_static_lambda_content = child.lambda_information.is_content_method_static; + + final String parent_class_name = parent.wrapper.getClassStruct().qualifiedName; + final String lambda_class_name = child.simpleName; + + final VarType lambda_class_type = new VarType(lambda_class_name, true); + + // this pointer + if (!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) { + meth.varproc.getThisvars().put(new VarVersionPaar(0, 0), parent_class_name); + meth.varproc.setVarName(new VarVersionPaar(0, 0), parent.simpleName + ".this"); + } + + // local variables + DirectGraph graph = encmeth.getOrBuildGraph(); + + final HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + + if (expr.type == Exprent.EXPRENT_NEW) { + NewExprent new_expr = (NewExprent)expr; + if (new_expr.isLambda() && lambda_class_type.equals(new_expr.getNewtype())) { + + InvocationExprent inv_dynamic = new_expr.getConstructor(); + + int param_index = is_static_lambda_content ? 0 : 1; + int varindex = is_static_lambda_content ? 0 : 1; + + for (int i = 0; i < vars_count; ++i) { + + Exprent param = inv_dynamic.getLstParameters().get(param_index + i); + + if (param.type == Exprent.EXPRENT_VAR) { + VarVersionPaar enc_varpaar = new VarVersionPaar((VarExprent)param); + String enc_varname = encmeth.varproc.getVarName(enc_varpaar); + + //meth.varproc.setVarName(new VarVersionPaar(varindex, 0), enc_varname); + mapNewNames.put(new VarVersionPaar(varindex, 0), enc_varname); + } + + varindex += md_content.params[i].stack_size; + } + } + } + } + + return 0; + } + }); + + // update names of local variables + HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { + meth.varproc.setVarName(entr.getKey(), entr.getValue()); + } + } + + private static void checkNotFoundClasses(ClassNode root, ClassNode node) { + + List<ClassNode> lstChildren = new ArrayList<ClassNode>(node.nested); + + for (ClassNode child : lstChildren) { + + if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) { + + Set<String> setEnclosing = child.enclosingClasses; + + if (setEnclosing.size() == 1) { + StructEnclosingMethodAttribute attr = + (StructEnclosingMethodAttribute)child.classStruct.getAttributes().getWithKey("EnclosingMethod"); + if (attr != null && attr.getMethodName() != null) { + if (node.classStruct.qualifiedName.equals(attr.getClassName()) && + node.classStruct.getMethod(attr.getMethodName(), attr.getMethodDescriptor()) != null) { + child.enclosingMethod = InterpreterUtil.makeUniqueKey(attr.getMethodName(), attr.getMethodDescriptor()); + continue; + } + } + } + + node.nested.remove(child); + child.parent = null; + setEnclosing.remove(node.classStruct.qualifiedName); + + boolean hasEnclosing = !setEnclosing.isEmpty(); + if (hasEnclosing) { + hasEnclosing = insertNestedClass(root, child); + } + + if (!hasEnclosing) { + if (child.type == ClassNode.CLASS_ANONYMOUS) { + DecompilerContext.getLogger() + .writeMessage("Unreferenced anonymous class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.Severity.WARN); + } + else if (child.type == ClassNode.CLASS_LOCAL) { + DecompilerContext.getLogger() + .writeMessage("Unreferenced local class " + child.classStruct.qualifiedName + "!", IFernflowerLogger.Severity.WARN); + } + } + } + } + } + + private static boolean insertNestedClass(ClassNode root, ClassNode child) { + + Set<String> setEnclosing = child.enclosingClasses; + + LinkedList<ClassNode> stack = new LinkedList<ClassNode>(); + stack.add(root); + + while (!stack.isEmpty()) { + + ClassNode node = stack.removeFirst(); + + if (setEnclosing.contains(node.classStruct.qualifiedName)) { + node.nested.add(child); + child.parent = node; + + return true; + } + + // note: ordered list + stack.addAll(node.nested); + } + + return false; + } + + + private static void computeLocalVarsAndDefinitions(final ClassNode node) { + + // local var masks + // class name, constructor descriptor, field mask + final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarMasks = new HashMap<String, HashMap<String, List<VarFieldPair>>>(); + + int cltypes = 0; + + for (ClassNode nd : node.nested) { + if (nd.type != ClassNode.CLASS_LAMBDA) { + if ((nd.access & CodeConstants.ACC_STATIC) == 0 && (nd.access & CodeConstants.ACC_INTERFACE) == 0) { + + cltypes |= nd.type; + + HashMap<String, List<VarFieldPair>> mask = getMaskLocalVars(nd.wrapper); + if (mask.isEmpty()) { + DecompilerContext.getLogger() + .writeMessage("Nested class " + nd.classStruct.qualifiedName + " has no constructor!", IFernflowerLogger.Severity.WARN); + } + else { + mapVarMasks.put(nd.classStruct.qualifiedName, mask); + } + } + } + } + + // local var masks + final HashMap<String, HashMap<String, List<VarFieldPair>>> mapVarFieldPairs = + new HashMap<String, HashMap<String, List<VarFieldPair>>>(); + + if (cltypes != ClassNode.CLASS_MEMBER) { + + // iterate enclosing class + for (final MethodWrapper meth : node.wrapper.getMethods()) { + + if (meth.root != null) { // neither abstract, nor native + DirectGraph graph = meth.getOrBuildGraph(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + for (Exprent expr : lst) { + + if (expr.type == Exprent.EXPRENT_NEW) { + InvocationExprent constr = ((NewExprent)expr).getConstructor(); + + if (constr != null && mapVarMasks.containsKey(constr.getClassname())) { // non-static inner class constructor + + String refclname = constr.getClassname(); + + ClassNode nestedClassNode = node.getClassNode(refclname); + + if (nestedClassNode.type != ClassNode.CLASS_MEMBER) { + + List<VarFieldPair> mask = mapVarMasks.get(refclname).get(constr.getStringDescriptor()); + + if (!mapVarFieldPairs.containsKey(refclname)) { + mapVarFieldPairs.put(refclname, new HashMap<String, List<VarFieldPair>>()); + } + + List<VarFieldPair> lstTemp = new ArrayList<VarFieldPair>(); + + for (int i = 0; i < mask.size(); i++) { + Exprent param = constr.getLstParameters().get(i); + VarFieldPair pair = null; + + if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) { + VarVersionPaar varpaar = new VarVersionPaar((VarExprent)param); + + // FIXME: final flags of variables are wrong! Correct the entire final functionality. + // if(meth.varproc.getVarFinal(varpaar) != VarTypeProcessor.VAR_NONFINAL) { + pair = new VarFieldPair(mask.get(i).keyfield, varpaar); + // } + } + + lstTemp.add(pair); + } + + List<VarFieldPair> pairmask = mapVarFieldPairs.get(refclname).get(constr.getStringDescriptor()); + + if (pairmask == null) { + pairmask = lstTemp; + } + else { + for (int i = 0; i < pairmask.size(); i++) { + if (!InterpreterUtil.equalObjects(pairmask.get(i), lstTemp.get(i))) { + pairmask.set(i, null); + } + } + } + + mapVarFieldPairs.get(refclname).put(constr.getStringDescriptor(), pairmask); + nestedClassNode.enclosingMethod = + InterpreterUtil.makeUniqueKey(meth.methodStruct.getName(), meth.methodStruct.getDescriptor()); + } + } + } + } + return 0; + } + }); + } + } + } + + // merge var masks + for (Entry<String, HashMap<String, List<VarFieldPair>>> entcl : mapVarMasks.entrySet()) { + + ClassNode nestedNode = node.getClassNode(entcl.getKey()); + + // intersection + List<VarFieldPair> intrPairMask = null; + // merge referenced constructors + if (mapVarFieldPairs.containsKey(entcl.getKey())) { + for (List<VarFieldPair> mask : mapVarFieldPairs.get(entcl.getKey()).values()) { + if (intrPairMask == null) { + intrPairMask = new ArrayList<VarFieldPair>(mask); + } + else { + mergeListSignatures(intrPairMask, mask, false); + } + } + } + + List<VarFieldPair> intrMask = null; + // merge all constructors + for (List<VarFieldPair> mask : entcl.getValue().values()) { + if (intrMask == null) { + intrMask = new ArrayList<VarFieldPair>(mask); + } + else { + mergeListSignatures(intrMask, mask, false); + } + } + + if (intrPairMask == null) { // member or local and never instantiated + intrPairMask = new ArrayList<VarFieldPair>(intrMask); + + boolean found = false; + + for (int i = 0; i < intrPairMask.size(); i++) { + if (intrPairMask.get(i) != null) { + if (found) { + intrPairMask.set(i, null); + } + found = true; + } + } + } + + mergeListSignatures(intrPairMask, intrMask, true); + + for (int i = 0; i < intrPairMask.size(); i++) { + VarFieldPair pair = intrPairMask.get(i); + if (pair != null && pair.keyfield.length() > 0) { + nestedNode.mapFieldsToVars.put(pair.keyfield, pair.varpaar); + } + } + + // set resulting constructor signatures + for (Entry<String, List<VarFieldPair>> entmt : entcl.getValue().entrySet()) { + mergeListSignatures(entmt.getValue(), intrPairMask, false); + + MethodWrapper meth = nestedNode.wrapper.getMethodWrapper("<init>", entmt.getKey()); + meth.signatureFields = new ArrayList<VarVersionPaar>(); + + for (VarFieldPair pair : entmt.getValue()) { + meth.signatureFields.add(pair == null ? null : pair.varpaar); + } + } + } + } + + private static void insertLocalVars(final ClassNode parent, final ClassNode child) { + + // enclosing method, is null iff member class + MethodWrapper encmeth = parent.wrapper.getMethods().getWithKey(child.enclosingMethod); + + // iterate all child methods + for (final MethodWrapper meth : child.wrapper.getMethods()) { + + if (meth.root != null) { // neither abstract nor native + + // local var names + HashMap<VarVersionPaar, String> mapNewNames = new HashMap<VarVersionPaar, String>(); + // local var types + HashMap<VarVersionPaar, VarType> mapNewTypes = new HashMap<VarVersionPaar, VarType>(); + + final HashMap<Integer, VarVersionPaar> mapParamsToNewVars = new HashMap<Integer, VarVersionPaar>(); + if (meth.signatureFields != null) { + int index = 0; + int varindex = 1; + MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor()); + + for (VarVersionPaar paar : meth.signatureFields) { + if (paar != null) { + VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + + mapParamsToNewVars.put(varindex, newvar); + + String varname = null; + VarType vartype = null; + + if (child.type != ClassNode.CLASS_MEMBER) { + varname = encmeth.varproc.getVarName(paar); + vartype = encmeth.varproc.getVarType(paar); + + encmeth.varproc.setVarFinal(paar, VarTypeProcessor.VAR_FINALEXPLICIT); + } + + if (paar.var == -1 || "this".equals(varname)) { + if (parent.simpleName == null) { + // anonymous enclosing class, no access to this + varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + } + else { + varname = parent.simpleName + ".this"; + } + meth.varproc.getThisvars().put(newvar, parent.classStruct.qualifiedName); + } + + mapNewNames.put(newvar, varname); + mapNewTypes.put(newvar, vartype); + } + varindex += md.params[index++].stack_size; + } + } + + // new vars + final HashMap<String, VarVersionPaar> mapFieldsToNewVars = new HashMap<String, VarVersionPaar>(); + + for (ClassNode clnode = child; clnode != null; clnode = clnode.parent) { + + for (Entry<String, VarVersionPaar> entr : clnode.mapFieldsToVars.entrySet()) { + VarVersionPaar newvar = new VarVersionPaar(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0); + + mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(clnode.classStruct.qualifiedName, entr.getKey()), newvar); + + String varname = null; + VarType vartype = null; + + if (clnode.type != ClassNode.CLASS_MEMBER) { + + MethodWrapper enclosing_method = clnode.parent.wrapper.getMethods().getWithKey(clnode.enclosingMethod); + + varname = enclosing_method.varproc.getVarName(entr.getValue()); + vartype = enclosing_method.varproc.getVarType(entr.getValue()); + + enclosing_method.varproc.setVarFinal(entr.getValue(), VarTypeProcessor.VAR_FINALEXPLICIT); + } + + if (entr.getValue().var == -1 || "this".equals(varname)) { + if (clnode.parent.simpleName == null) { + // anonymous enclosing class, no access to this + varname = VarExprent.VAR_NAMELESS_ENCLOSURE; + } + else { + varname = clnode.parent.simpleName + ".this"; + } + meth.varproc.getThisvars().put(newvar, clnode.parent.classStruct.qualifiedName); + } + + mapNewNames.put(newvar, varname); + mapNewTypes.put(newvar, vartype); + + // hide synthetic field + if (clnode == child) { // fields higher up the chain were already handled with their classes + StructField fd = child.classStruct.getFields().getWithKey(entr.getKey()); + child.wrapper.getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor())); + } + } + } + + HashSet<String> setNewOuterNames = new HashSet<String>(mapNewNames.values()); + setNewOuterNames.removeAll(meth.setOuterVarNames); + + meth.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames)); + meth.setOuterVarNames.addAll(setNewOuterNames); + + for (Entry<VarVersionPaar, String> entr : mapNewNames.entrySet()) { + VarVersionPaar varpaar = entr.getKey(); + VarType vartype = mapNewTypes.get(varpaar); + + meth.varproc.setVarName(varpaar, entr.getValue()); + if (vartype != null) { + meth.varproc.setVarType(varpaar, vartype); + } + } + + DirectGraph graph = meth.getOrBuildGraph(); + + graph.iterateExprents(new DirectGraph.ExprentIterator() { + public int processExprent(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)asexpr.getLeft(); + + if (fexpr.getClassname().equals(child.classStruct.qualifiedName) && // process this class only + mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(child.classStruct.qualifiedName, + InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr + .getDescriptor().descriptorString)))) { + return 2; + } + + //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && + // mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString))) { + // return 2; + //} + } + } + + if (child.type == ClassNode.CLASS_ANONYMOUS && "<init>".equals(meth.methodStruct.getName()) + && exprent.type == Exprent.EXPRENT_INVOCATION) { + InvocationExprent invexpr = (InvocationExprent)exprent; + if (invexpr.getFunctype() == InvocationExprent.TYP_INIT) { + // invocation of the super constructor in an anonymous class + child.superInvocation = invexpr; // FIXME: save original names of parameters + return 2; + } + } + + replaceExprent(exprent); + + return 0; + } + + private Exprent replaceExprent(Exprent exprent) { + + if (exprent.type == Exprent.EXPRENT_VAR) { + int varindex = ((VarExprent)exprent).getIndex(); + if (mapParamsToNewVars.containsKey(varindex)) { + VarVersionPaar newvar = mapParamsToNewVars.get(varindex); + meth.varproc.getExternvars().add(newvar); + return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + } + } + else if (exprent.type == Exprent.EXPRENT_FIELD) { + FieldExprent fexpr = (FieldExprent)exprent; + + String keyField = InterpreterUtil.makeUniqueKey(fexpr.getClassname(), InterpreterUtil + .makeUniqueKey(fexpr.getName(), fexpr.getDescriptor().descriptorString)); + + if (mapFieldsToNewVars.containsKey(keyField)) { + //if(fexpr.getClassname().equals(child.classStruct.qualifiedName) && + // mapFieldsToNewVars.containsKey(keyField)) { + VarVersionPaar newvar = mapFieldsToNewVars.get(keyField); + meth.varproc.getExternvars().add(newvar); + return new VarExprent(newvar.var, meth.varproc.getVarType(newvar), meth.varproc); + } + } + + boolean replaced = true; + while (replaced) { + replaced = false; + + for (Exprent expr : exprent.getAllExprents()) { + Exprent retexpr = replaceExprent(expr); + if (retexpr != null) { + exprent.replaceExprent(expr, retexpr); + replaced = true; + break; + } + } + } + + return null; + } + }); + } + } + } + + private static HashMap<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wrapper) { + + HashMap<String, List<VarFieldPair>> mapMasks = new HashMap<String, List<VarFieldPair>>(); + + StructClass cl = wrapper.getClassStruct(); + + // iterate over constructors + for (StructMethod mt : cl.getMethods()) { + if ("<init>".equals(mt.getName())) { + + MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor()); + + MethodWrapper meth = wrapper.getMethodWrapper("<init>", mt.getDescriptor()); + DirectGraph graph = meth.getOrBuildGraph(); + + if (graph != null) { // something gone wrong, should not be null + List<VarFieldPair> fields = new ArrayList<VarFieldPair>(); + + int varindex = 1; + for (int i = 0; i < md.params.length; i++) { // no static methods allowed + String keyField = getEnclosingVarField(cl, meth, graph, varindex); + fields.add(keyField == null ? null : new VarFieldPair(keyField, new VarVersionPaar(-1, 0))); // TODO: null? + varindex += md.params[i].stack_size; + } + mapMasks.put(mt.getDescriptor(), fields); + } + } + } + + return mapMasks; + } + + private static String getEnclosingVarField(StructClass cl, MethodWrapper meth, DirectGraph graph, final int index) { + + String field = ""; + + // parameter variable final + if (meth.varproc.getVarFinal(new VarVersionPaar(index, 0)) == VarTypeProcessor.VAR_NONFINAL) { + return null; + } + + boolean noSynthFlag = DecompilerContext.getOption(IFernflowerPreferences.SYNTHETIC_NOT_SET); + + // no loop at the begin + DirectNode firstnode = graph.first; + if (firstnode.preds.isEmpty()) { + // assignment to a final synthetic field? + for (Exprent exprent : firstnode.exprents) { + if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) { + AssignmentExprent asexpr = (AssignmentExprent)exprent; + if (asexpr.getRight().type == Exprent.EXPRENT_VAR && ((VarExprent)asexpr.getRight()).getIndex() == index) { + if (asexpr.getLeft().type == Exprent.EXPRENT_FIELD) { + + FieldExprent left = (FieldExprent)asexpr.getLeft(); + StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString); + + if (fd != null) { // local (== not inherited) field + if (cl.qualifiedName.equals(left.getClassname()) && + fd.hasModifier(CodeConstants.ACC_FINAL) && + (fd.isSynthetic() || (noSynthFlag && fd.hasModifier(CodeConstants.ACC_PRIVATE)))) { + field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString); + break; + } + } + } + } + } + } + } + + return field; + } + + private static void mergeListSignatures(List<VarFieldPair> first, List<VarFieldPair> second, boolean both) { + + int i = 1; + while (true) { + if (first.size() <= i || second.size() <= i) { + break; + } + + VarFieldPair fobj = first.get(first.size() - i); + VarFieldPair sobj = second.get(second.size() - i); + + boolean eq = false; + if (fobj == null || sobj == null) { + eq = (fobj == sobj); + } + else { + eq = true; + if (fobj.keyfield.length() == 0) { + fobj.keyfield = sobj.keyfield; + } + else if (sobj.keyfield.length() == 0) { + if (both) { + sobj.keyfield = fobj.keyfield; + } + } + else { + eq = fobj.keyfield.equals(sobj.keyfield); + } + } + + if (!eq) { + first.set(first.size() - i, null); + if (both) { + second.set(second.size() - i, null); + } + } + else { + if (fobj != null) { + if (fobj.varpaar.var == -1) { + fobj.varpaar = sobj.varpaar; + } + else { + sobj.varpaar = fobj.varpaar; + } + } + } + i++; + } + + for (int j = 1; j <= first.size() - i; j++) { + first.set(j, null); + } + + if (both) { + for (int j = 1; j <= second.size() - i; j++) { + second.set(j, null); + } + } + + // first + if (first.isEmpty()) { + if (!second.isEmpty() && both) { + second.set(0, null); + } + } + else if (second.isEmpty()) { + first.set(0, null); + } + else { + VarFieldPair fobj = first.get(0); + VarFieldPair sobj = second.get(0); + + boolean eq = false; + if (fobj == null || sobj == null) { + eq = (fobj == sobj); + } + else { + eq = true; + if (fobj.keyfield.length() == 0) { + fobj.keyfield = sobj.keyfield; + } + else if (sobj.keyfield.length() == 0) { + if (both) { + sobj.keyfield = fobj.keyfield; + } + } + else { + eq = fobj.keyfield.equals(sobj.keyfield); + } + } + + if (!eq) { + first.set(0, null); + if (both) { + second.set(0, null); + } + } + else if (fobj != null) { + if (fobj.varpaar.var == -1) { + fobj.varpaar = sobj.varpaar; + } + else { + sobj.varpaar = fobj.varpaar; + } + } + } + } + + + private static void setLocalClassDefinition(MethodWrapper meth, ClassNode node) { + + RootStatement root = meth.root; + + HashSet<Statement> setStats = new HashSet<Statement>(); + VarType classtype = new VarType(node.classStruct.qualifiedName, true); + + Statement stdef = getDefStatement(root, classtype, setStats); + if (stdef == null) { + // unreferenced local class + stdef = root.getFirst(); + } + + Statement first = findFirstBlock(stdef, setStats); + + List<Exprent> lst; + if (first == null) { + lst = stdef.getVarDefinitions(); + } + else if (first.getExprents() == null) { + lst = first.getVarDefinitions(); + } + else { + lst = first.getExprents(); + } + + + int addindex = 0; + for (Exprent expr : lst) { + if (searchForClass(expr, classtype)) { + break; + } + addindex++; + } + + VarExprent var = new VarExprent(meth.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), + classtype, meth.varproc); + var.setDefinition(true); + var.setClassdef(true); + + lst.add(addindex, var); + } + + + private static Statement findFirstBlock(Statement stat, HashSet<Statement> setStats) { + + LinkedList<Statement> stack = new LinkedList<Statement>(); + stack.add(stat); + + while (!stack.isEmpty()) { + Statement st = stack.remove(0); + + if (stack.isEmpty() || setStats.contains(st)) { + + if (st.isLabeled() && !stack.isEmpty()) { + return st; + } + + if (st.getExprents() != null) { + return st; + } + else { + stack.clear(); + + switch (st.type) { + case Statement.TYPE_SEQUENCE: + stack.addAll(0, st.getStats()); + break; + case Statement.TYPE_IF: + case Statement.TYPE_ROOT: + case Statement.TYPE_SWITCH: + case Statement.TYPE_SYNCRONIZED: + stack.add(st.getFirst()); + break; + default: + return st; + } + } + } + } + + return null; + } + + + private static Statement getDefStatement(Statement stat, VarType classtype, HashSet<Statement> setStats) { + + List<Exprent> condlst = new ArrayList<Exprent>(); + Statement retstat = null; + + if (stat.getExprents() == null) { + int counter = 0; + + for (Object obj : stat.getSequentialObjects()) { + if (obj instanceof Statement) { + Statement st = (Statement)obj; + + Statement stTemp = getDefStatement(st, classtype, setStats); + + if (stTemp != null) { + if (counter == 1) { + retstat = stat; + break; + } + retstat = stTemp; + counter++; + } + + if (st.type == DoStatement.TYPE_DO) { + DoStatement dost = (DoStatement)st; + + condlst.addAll(dost.getInitExprentList()); + condlst.addAll(dost.getConditionExprentList()); + } + } + else if (obj instanceof Exprent) { + condlst.add((Exprent)obj); + } + } + } + else { + condlst = stat.getExprents(); + } + + if (retstat != stat) { + for (Exprent exprent : condlst) { + if (exprent != null && searchForClass(exprent, classtype)) { + retstat = stat; + break; + } + } + } + + if (retstat != null) { + setStats.add(stat); + } + + return retstat; + } + + private static boolean searchForClass(Exprent exprent, VarType classtype) { + + List<Exprent> lst = exprent.getAllExprents(true); + lst.add(exprent); + + String classname = classtype.value; + + for (Exprent expr : lst) { + + boolean res = false; + + switch (expr.type) { + case Exprent.EXPRENT_CONST: + ConstExprent cexpr = (ConstExprent)expr; + res = (VarType.VARTYPE_CLASS.equals(cexpr.getConsttype()) && classname.equals(cexpr.getValue()) || + classtype.equals(cexpr.getConsttype())); + break; + case Exprent.EXPRENT_FIELD: + res = classname.equals(((FieldExprent)expr).getClassname()); + break; + case Exprent.EXPRENT_INVOCATION: + res = classname.equals(((InvocationExprent)expr).getClassname()); + break; + case Exprent.EXPRENT_NEW: + VarType newType = expr.getExprType(); + res = newType.type == CodeConstants.TYPE_OBJECT && classname.equals(newType.value); + break; + case Exprent.EXPRENT_VAR: + VarExprent vexpr = (VarExprent)expr; + if (vexpr.isDefinition()) { + VarType vtype = vexpr.getVartype(); + if (classtype.equals(vtype) || (vtype.arraydim > 0 && classtype.value.equals(vtype.value))) { + res = true; + } + } + } + + if (res) { + return true; + } + } + + return false; + } + + + private static class VarFieldPair { + + public String keyfield = ""; + public VarVersionPaar varpaar; + + public VarFieldPair(String field, VarVersionPaar varpaar) { + this.keyfield = field; + this.varpaar = varpaar; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (o == null || !(o instanceof VarFieldPair)) return false; + + VarFieldPair pair = (VarFieldPair)o; + return keyfield.equals(pair.keyfield) && varpaar.equals(pair.varpaar); + } + + @Override + public int hashCode() { + return keyfield.hashCode() + varpaar.hashCode(); + } + } +} |