fernflower/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java

1030 lines
35 KiB
Java

/*
* 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.VarVersionPair;
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.lambdaInformation.is_method_reference) {
ClassNode node_content = DecompilerContext.getClassProcessor().getMapRootClasses().get(node.classStruct.qualifiedName);
if (node_content != null && node_content.getWrapper() != null) {
node_content.getWrapper().getHiddenMembers().add(node.lambdaInformation.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 {
String message = "Nameless local or member class " + cl.qualifiedName + "!";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
child.simpleName = "NamelessClass_" + (++nameless);
}
child.namelessConstructorStub = !cl.hasModifier(CodeConstants.ACC_STATIC) && cl.getMethods().size() + cl.getFields().size() == 0;
}
}
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.getWrapper().getMethods().getWithKey(child.enclosingMethod), child);
}
}
}
for (ClassNode child : node.nested) {
processClass(root, child);
}
}
private static void setLambdaVars(ClassNode parent, ClassNode child) {
if (child.lambdaInformation.is_method_reference) { // method reference, no code and no parameters
return;
}
final MethodWrapper meth = parent.getWrapper().getMethods().getWithKey(child.lambdaInformation.content_method_key);
final MethodWrapper encmeth = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod);
MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambdaInformation.method_descriptor);
final MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambdaInformation.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.lambdaInformation.is_content_method_static;
final String parent_class_name = parent.getWrapper().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 VarVersionPair(0, 0), parent_class_name);
meth.varproc.setVarName(new VarVersionPair(0, 0), parent.simpleName + ".this");
}
// local variables
DirectGraph graph = encmeth.getOrBuildGraph();
final HashMap<VarVersionPair, String> mapNewNames = new HashMap<VarVersionPair, 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) {
VarVersionPair enc_varpaar = new VarVersionPair((VarExprent)param);
String enc_varname = encmeth.varproc.getVarName(enc_varpaar);
//meth.varproc.setVarName(new VarVersionPair(varindex, 0), enc_varname);
mapNewNames.put(new VarVersionPair(varindex, 0), enc_varname);
}
varindex += md_content.params[i].stackSize;
}
}
}
}
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<VarVersionPair, 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) {
String message = "Unreferenced anonymous class " + child.classStruct.qualifiedName + "!";
DecompilerContext.getLogger().writeMessage(message, IFernflowerLogger.Severity.WARN);
}
else if (child.type == ClassNode.CLASS_LOCAL) {
String message = "Unreferenced local class " + child.classStruct.qualifiedName + "!";
DecompilerContext.getLogger().writeMessage(message, 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.getWrapper());
if (mask.isEmpty()) {
String message = "Nested class " + nd.classStruct.qualifiedName + " has no constructor!";
DecompilerContext.getLogger().writeMessage(message, 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.getWrapper().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) {
VarVersionPair varpaar = new VarVersionPair((VarExprent)param);
// FIXME: final flags of variables are wrong! Correct the entire final functionality.
// if(meth.varproc.getVarFinal(varpaar) != VarTypeProcessor.VAR_NON_FINAL) {
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.getWrapper().getMethodWrapper("<init>", entmt.getKey());
meth.signatureFields = new ArrayList<VarVersionPair>();
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.getWrapper().getMethods().getWithKey(child.enclosingMethod);
// iterate all child methods
for (final MethodWrapper meth : child.getWrapper().getMethods()) {
if (meth.root != null) { // neither abstract nor native
// local var names
HashMap<VarVersionPair, String> mapNewNames = new HashMap<VarVersionPair, String>();
// local var types
HashMap<VarVersionPair, VarType> mapNewTypes = new HashMap<VarVersionPair, VarType>();
final HashMap<Integer, VarVersionPair> mapParamsToNewVars = new HashMap<Integer, VarVersionPair>();
if (meth.signatureFields != null) {
int index = 0;
int varindex = 1;
MethodDescriptor md = MethodDescriptor.parseDescriptor(meth.methodStruct.getDescriptor());
for (VarVersionPair paar : meth.signatureFields) {
if (paar != null) {
VarVersionPair newvar = new VarVersionPair(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_EXPLICIT_FINAL);
}
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++].stackSize;
}
}
// new vars
final HashMap<String, VarVersionPair> mapFieldsToNewVars = new HashMap<String, VarVersionPair>();
for (ClassNode clnode = child; clnode != null; clnode = clnode.parent) {
for (Entry<String, VarVersionPair> entr : clnode.mapFieldsToVars.entrySet()) {
VarVersionPair newvar = new VarVersionPair(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.getWrapper().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_EXPLICIT_FINAL);
}
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.getWrapper().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<VarVersionPair, String> entr : mapNewNames.entrySet()) {
VarVersionPair 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)) {
VarVersionPair newvar = mapParamsToNewVars.get(varindex);
meth.varproc.getExternalVars().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)) {
VarVersionPair newvar = mapFieldsToNewVars.get(keyField);
meth.varproc.getExternalVars().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 VarVersionPair(-1, 0))); // TODO: null?
varindex += md.params[i].stackSize;
}
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 VarVersionPair(index, 0)) == VarTypeProcessor.VAR_NON_FINAL) {
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 VarVersionPair varpaar;
public VarFieldPair(String field, VarVersionPair 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();
}
}
}