Fork of the Fernflower decompiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fernflower/src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java

924 lines
33 KiB

// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
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) {
StructClass cl = child.classStruct;
// ensure not-empty class name
if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_MEMBER) && child.simpleName == null) {
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);
}
}
}
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;
}
MethodWrapper method = parent.getWrapper().getMethods().getWithKey(child.lambdaInformation.content_method_key);
MethodWrapper enclosingMethod = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod);
MethodDescriptor md_lambda = MethodDescriptor.parseDescriptor(child.lambdaInformation.method_descriptor);
MethodDescriptor md_content = MethodDescriptor.parseDescriptor(child.lambdaInformation.content_method_descriptor);
int vars_count = md_content.params.length - md_lambda.params.length;
boolean is_static_lambda_content = child.lambdaInformation.is_content_method_static;
String parent_class_name = parent.getWrapper().getClassStruct().qualifiedName;
String lambda_class_name = child.simpleName;
VarType lambda_class_type = new VarType(lambda_class_name, true);
// this pointer
if (!is_static_lambda_content && DecompilerContext.getOption(IFernflowerPreferences.LAMBDA_TO_ANONYMOUS_CLASS)) {
method.varproc.getThisVars().put(new VarVersionPair(0, 0), parent_class_name);
method.varproc.setVarName(new VarVersionPair(0, 0), parent.simpleName + ".this");
}
Map<VarVersionPair, String> mapNewNames = new HashMap<>();
enclosingMethod.getOrBuildGraph().iterateExprents(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;
VarNamesCollector enclosingCollector = new VarNamesCollector(enclosingMethod.varproc.getVarNames());
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 < md_content.params.length; ++i) {
VarVersionPair varVersion = new VarVersionPair(varIndex, 0);
if (i < vars_count) {
Exprent param = inv_dynamic.getLstParameters().get(param_index + i);
if (param.type == Exprent.EXPRENT_VAR) {
mapNewNames.put(varVersion, enclosingMethod.varproc.getVarName(new VarVersionPair((VarExprent)param)));
}
}
else {
mapNewNames.put(varVersion, enclosingCollector.getFreeName(method.varproc.getVarName(varVersion)));
}
varIndex += md_content.params[i].stackSize;
}
}
}
}
return 0;
});
// update names of local variables
Set<String> setNewOuterNames = new HashSet<>(mapNewNames.values());
setNewOuterNames.removeAll(method.setOuterVarNames);
method.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames));
method.setOuterVarNames.addAll(setNewOuterNames);
for (Entry<VarVersionPair, String> entry : mapNewNames.entrySet()) {
method.varproc.setVarName(entry.getKey(), entry.getValue());
}
}
private static void checkNotFoundClasses(ClassNode root, ClassNode node) {
List<ClassNode> copy = new ArrayList<>(node.nested);
for (ClassNode child : copy) {
if (child.classStruct.isSynthetic()) {
continue;
}
if ((child.type == ClassNode.CLASS_LOCAL || child.type == ClassNode.CLASS_ANONYMOUS) && child.enclosingMethod == null) {
Set<String> setEnclosing = child.enclosingClasses;
if (!setEnclosing.isEmpty()) {
StructEnclosingMethodAttribute attr =
(StructEnclosingMethodAttribute)child.classStruct.getAttribute("EnclosingMethod");
if (attr != null &&
attr.getMethodName() != null &&
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() && 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<>();
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(ClassNode node) {
// class name -> constructor descriptor -> var to field link
Map<String, Map<String, List<VarFieldPair>>> mapVarMasks = new HashMap<>();
int clTypes = 0;
for (ClassNode nd : node.nested) {
if (nd.type != ClassNode.CLASS_LAMBDA &&
!nd.classStruct.isSynthetic() &&
(nd.access & CodeConstants.ACC_STATIC) == 0 &&
(nd.access & CodeConstants.ACC_INTERFACE) == 0) {
clTypes |= nd.type;
Map<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
Map<String, Map<String, List<VarFieldPair>>> mapVarFieldPairs = new HashMap<>();
if (clTypes != ClassNode.CLASS_MEMBER) {
// iterate enclosing class
for (MethodWrapper method : node.getWrapper().getMethods()) {
if (method.root != null) { // neither abstract, nor native
method.getOrBuildGraph().iterateExprents(exprent -> {
List<Exprent> lst = exprent.getAllExprents(true);
lst.add(exprent);
for (Exprent expr : lst) {
if (expr.type == Exprent.EXPRENT_NEW) {
InvocationExprent constructor = ((NewExprent)expr).getConstructor();
if (constructor != null && mapVarMasks.containsKey(constructor.getClassname())) { // non-static inner class constructor
String refClassName = constructor.getClassname();
ClassNode nestedClassNode = node.getClassNode(refClassName);
if (nestedClassNode.type != ClassNode.CLASS_MEMBER) {
List<VarFieldPair> mask = mapVarMasks.get(refClassName).get(constructor.getStringDescriptor());
if (!mapVarFieldPairs.containsKey(refClassName)) {
mapVarFieldPairs.put(refClassName, new HashMap<>());
}
List<VarFieldPair> lstTemp = new ArrayList<>();
for (int i = 0; i < mask.size(); i++) {
Exprent param = constructor.getLstParameters().get(i);
VarFieldPair pair = null;
if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) {
VarVersionPair varPair = new VarVersionPair((VarExprent)param);
// FIXME: flags of variables are wrong! Correct the entire functionality.
// if(method.varproc.getVarFinal(varPair) != VarTypeProcessor.VAR_NON_FINAL) {
pair = new VarFieldPair(mask.get(i).fieldKey, varPair);
// }
}
lstTemp.add(pair);
}
List<VarFieldPair> pairMask = mapVarFieldPairs.get(refClassName).get(constructor.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(refClassName).put(constructor.getStringDescriptor(), pairMask);
nestedClassNode.enclosingMethod =
InterpreterUtil.makeUniqueKey(method.methodStruct.getName(), method.methodStruct.getDescriptor());
}
}
}
}
return 0;
});
}
}
}
// merge var masks
for (Entry<String, Map<String, List<VarFieldPair>>> enclosing : mapVarMasks.entrySet()) {
ClassNode nestedNode = node.getClassNode(enclosing.getKey());
// intersection
List<VarFieldPair> interPairMask = null;
// merge referenced constructors
if (mapVarFieldPairs.containsKey(enclosing.getKey())) {
for (List<VarFieldPair> mask : mapVarFieldPairs.get(enclosing.getKey()).values()) {
if (interPairMask == null) {
interPairMask = new ArrayList<>(mask);
}
else {
mergeListSignatures(interPairMask, mask, false);
}
}
}
List<VarFieldPair> interMask = null;
// merge all constructors
for (List<VarFieldPair> mask : enclosing.getValue().values()) {
if (interMask == null) {
interMask = new ArrayList<>(mask);
}
else {
mergeListSignatures(interMask, mask, false);
}
}
if (interPairMask == null) { // member or local and never instantiated
interPairMask = interMask != null ? new ArrayList<>(interMask) : new ArrayList<>();
boolean found = false;
for (int i = 0; i < interPairMask.size(); i++) {
if (interPairMask.get(i) != null) {
if (found) {
interPairMask.set(i, null);
}
found = true;
}
}
}
mergeListSignatures(interPairMask, interMask, true);
for (VarFieldPair pair : interPairMask) {
if (pair != null && !pair.fieldKey.isEmpty()) {
nestedNode.mapFieldsToVars.put(pair.fieldKey, pair.varPair);
}
}
// set resulting constructor signatures
for (Entry<String, List<VarFieldPair>> entry : enclosing.getValue().entrySet()) {
mergeListSignatures(entry.getValue(), interPairMask, false);
List<VarVersionPair> mask = new ArrayList<>(entry.getValue().size());
for (VarFieldPair pair : entry.getValue()) {
mask.add(pair != null && !pair.fieldKey.isEmpty() ? pair.varPair : null);
}
nestedNode.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, entry.getKey()).synthParameters = mask;
}
}
}
private static void insertLocalVars(ClassNode parent, ClassNode child) {
// enclosing method, is null iff member class
MethodWrapper enclosingMethod = parent.getWrapper().getMethods().getWithKey(child.enclosingMethod);
// iterate all child methods
for (MethodWrapper method : child.getWrapper().getMethods()) {
if (method.root != null) { // neither abstract nor native
Map<VarVersionPair, String> mapNewNames = new HashMap<>(); // local var names
Map<VarVersionPair, VarType> mapNewTypes = new HashMap<>(); // local var types
Map<Integer, VarVersionPair> mapParamsToNewVars = new HashMap<>();
if (method.synthParameters != null) {
int index = 0, varIndex = 1;
MethodDescriptor md = MethodDescriptor.parseDescriptor(method.methodStruct.getDescriptor());
for (VarVersionPair pair : method.synthParameters) {
if (pair != null) {
VarVersionPair newVar = new VarVersionPair(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0);
mapParamsToNewVars.put(varIndex, newVar);
String varName = null;
VarType varType = null;
if (child.type != ClassNode.CLASS_MEMBER) {
varName = enclosingMethod.varproc.getVarName(pair);
varType = enclosingMethod.varproc.getVarType(pair);
enclosingMethod.varproc.setVarFinal(pair, VarTypeProcessor.VAR_EXPLICIT_FINAL);
}
if (pair.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";
}
method.varproc.getThisVars().put(newVar, parent.classStruct.qualifiedName);
}
mapNewNames.put(newVar, varName);
mapNewTypes.put(newVar, varType);
}
varIndex += md.params[index++].stackSize;
}
}
Map<String, VarVersionPair> mapFieldsToNewVars = new HashMap<>();
for (ClassNode classNode = child; classNode != null; classNode = classNode.parent) {
for (Entry<String, VarVersionPair> entry : classNode.mapFieldsToVars.entrySet()) {
VarVersionPair newVar = new VarVersionPair(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), 0);
mapFieldsToNewVars.put(InterpreterUtil.makeUniqueKey(classNode.classStruct.qualifiedName, entry.getKey()), newVar);
String varName = null;
VarType varType = null;
if (classNode.type != ClassNode.CLASS_MEMBER) {
MethodWrapper enclosing_method = classNode.parent.getWrapper().getMethods().getWithKey(classNode.enclosingMethod);
varName = enclosing_method.varproc.getVarName(entry.getValue());
varType = enclosing_method.varproc.getVarType(entry.getValue());
enclosing_method.varproc.setVarFinal(entry.getValue(), VarTypeProcessor.VAR_EXPLICIT_FINAL);
}
if (entry.getValue().var == -1 || "this".equals(varName)) {
if (classNode.parent.simpleName == null) {
// anonymous enclosing class, no access to this
varName = VarExprent.VAR_NAMELESS_ENCLOSURE;
}
else {
varName = classNode.parent.simpleName + ".this";
}
method.varproc.getThisVars().put(newVar, classNode.parent.classStruct.qualifiedName);
}
mapNewNames.put(newVar, varName);
mapNewTypes.put(newVar, varType);
// hide synthetic field
if (classNode == child) { // fields higher up the chain were already handled with their classes
StructField fd = child.classStruct.getFields().getWithKey(entry.getKey());
child.getWrapper().getHiddenMembers().add(InterpreterUtil.makeUniqueKey(fd.getName(), fd.getDescriptor()));
}
}
}
Set<String> setNewOuterNames = new HashSet<>(mapNewNames.values());
setNewOuterNames.removeAll(method.setOuterVarNames);
method.varproc.refreshVarNames(new VarNamesCollector(setNewOuterNames));
method.setOuterVarNames.addAll(setNewOuterNames);
for (Entry<VarVersionPair, String> entry : mapNewNames.entrySet()) {
VarVersionPair pair = entry.getKey();
VarType type = mapNewTypes.get(pair);
method.varproc.setVarName(pair, entry.getValue());
if (type != null) {
method.varproc.setVarType(pair, type);
}
}
method.getOrBuildGraph().iterateExprents(new DirectGraph.ExprentIterator() {
@Override
public int processExprent(Exprent exprent) {
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent fExpr = (FieldExprent)assignExpr.getLeft();
String qName = child.classStruct.qualifiedName;
if (fExpr.getClassname().equals(qName) && // process this class only
mapFieldsToNewVars.containsKey(InterpreterUtil.makeUniqueKey(qName, fExpr.getName(), fExpr.getDescriptor().descriptorString))) {
return 2;
}
}
}
if (child.type == ClassNode.CLASS_ANONYMOUS &&
CodeConstants.INIT_NAME.equals(method.methodStruct.getName()) &&
exprent.type == Exprent.EXPRENT_INVOCATION) {
InvocationExprent invokeExpr = (InvocationExprent)exprent;
if (invokeExpr.getFunctype() == InvocationExprent.TYP_INIT) {
// invocation of the super constructor in an anonymous class
child.superInvocation = invokeExpr; // 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);
method.varproc.getExternalVars().add(newVar);
return new VarExprent(newVar.var, method.varproc.getVarType(newVar), method.varproc);
}
}
else if (exprent.type == Exprent.EXPRENT_FIELD) {
FieldExprent fExpr = (FieldExprent)exprent;
String key = InterpreterUtil.makeUniqueKey(fExpr.getClassname(), fExpr.getName(), fExpr.getDescriptor().descriptorString);
if (mapFieldsToNewVars.containsKey(key)) {
//if(fExpr.getClassname().equals(child.classStruct.qualifiedName) &&
// mapFieldsToNewVars.containsKey(key)) {
VarVersionPair newVar = mapFieldsToNewVars.get(key);
method.varproc.getExternalVars().add(newVar);
return new VarExprent(newVar.var, method.varproc.getVarType(newVar), method.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 Map<String, List<VarFieldPair>> getMaskLocalVars(ClassWrapper wrapper) {
Map<String, List<VarFieldPair>> mapMasks = new HashMap<>();
StructClass cl = wrapper.getClassStruct();
// iterate over constructors
for (StructMethod mt : cl.getMethods()) {
if (CodeConstants.INIT_NAME.equals(mt.getName())) {
MethodDescriptor md = MethodDescriptor.parseDescriptor(mt.getDescriptor());
MethodWrapper method = wrapper.getMethodWrapper(CodeConstants.INIT_NAME, mt.getDescriptor());
DirectGraph graph = method.getOrBuildGraph();
if (graph != null) { // something gone wrong, should not be null
List<VarFieldPair> fields = new ArrayList<>(md.params.length);
int varIndex = 1;
for (int i = 0; i < md.params.length; i++) { // no static methods allowed
String keyField = getEnclosingVarField(cl, method, 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 method, DirectGraph graph, int index) {
String field = "";
// parameter variable final
if (method.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 synthetic field?
for (Exprent exprent : firstNode.exprents) {
if (exprent.type == Exprent.EXPRENT_ASSIGNMENT) {
AssignmentExprent assignExpr = (AssignmentExprent)exprent;
if (assignExpr.getRight().type == Exprent.EXPRENT_VAR &&
((VarExprent)assignExpr.getRight()).getIndex() == index &&
assignExpr.getLeft().type == Exprent.EXPRENT_FIELD) {
FieldExprent left = (FieldExprent)assignExpr.getLeft();
StructField fd = cl.getField(left.getName(), left.getDescriptor().descriptorString);
if (fd != null &&
cl.qualifiedName.equals(left.getClassname()) &&
(fd.isSynthetic() || noSynthFlag && possiblySyntheticField(fd))) {
// local (== not inherited) field
field = InterpreterUtil.makeUniqueKey(left.getName(), left.getDescriptor().descriptorString);
break;
}
}
}
}
}
return field;
}
private static boolean possiblySyntheticField(StructField fd) {
return fd.getName().contains("$") && fd.hasModifier(CodeConstants.ACC_FINAL) && fd.hasModifier(CodeConstants.ACC_PRIVATE);
}
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);
if (!isEqual(both, fObj, sObj)) {
first.set(first.size() - i, null);
if (both) {
second.set(second.size() - i, null);
}
}
else if (fObj != null) {
if (fObj.varPair.var == -1) {
fObj.varPair = sObj.varPair;
}
else {
sObj.varPair = fObj.varPair;
}
}
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);
if (!isEqual(both, fObj, sObj)) {
first.set(0, null);
if (both) {
second.set(0, null);
}
}
else if (fObj != null) {
if (fObj.varPair.var == -1) {
fObj.varPair = sObj.varPair;
}
else {
sObj.varPair = fObj.varPair;
}
}
}
}
private static boolean isEqual(boolean both, VarFieldPair fObj, VarFieldPair sObj) {
boolean eq;
if (fObj == null || sObj == null) {
eq = (fObj == sObj);
}
else {
eq = true;
if (fObj.fieldKey.length() == 0) {
fObj.fieldKey = sObj.fieldKey;
}
else if (sObj.fieldKey.length() == 0) {
if (both) {
sObj.fieldKey = fObj.fieldKey;
}
}
else {
eq = fObj.fieldKey.equals(sObj.fieldKey);
}
}
return eq;
}
private static void setLocalClassDefinition(MethodWrapper method, ClassNode node) {
RootStatement root = method.root;
Set<Statement> setStats = new HashSet<>();
VarType classType = new VarType(node.classStruct.qualifiedName, true);
Statement statement = getDefStatement(root, classType, setStats);
if (statement == null) {
// unreferenced local class
statement = root.getFirst();
}
Statement first = findFirstBlock(statement, setStats);
List<Exprent> lst;
//noinspection Duplicates
if (first == null) {
lst = statement.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(method.counter.getCounterAndIncrement(CounterContainer.VAR_COUNTER), classType, method.varproc);
var.setDefinition(true);
var.setClassDef(true);
lst.add(addIndex, var);
}
private static Statement findFirstBlock(Statement stat, Set<Statement> setStats) {
LinkedList<Statement> stack = new LinkedList<>();
stack.add(stat);
while (!stack.isEmpty()) {
Statement st = stack.remove(0);
if (stack.isEmpty() || setStats.contains(st)) {
if (st.isLabeled() && !stack.isEmpty() || st.getExprents() != null) {
return st;
}
stack.clear();
//noinspection Duplicates
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, Set<Statement> setStats) {
List<Exprent> lst = new ArrayList<>();
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 == Statement.TYPE_DO) {
DoStatement dost = (DoStatement)st;
lst.addAll(dost.getInitExprentList());
lst.addAll(dost.getConditionExprentList());
}
}
else if (obj instanceof Exprent) {
lst.add((Exprent)obj);
}
}
}
else {
lst = stat.getExprents();
}
if (retStat != stat) {
for (Exprent exprent : lst) {
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 constExpr = (ConstExprent)expr;
res = (VarType.VARTYPE_CLASS.equals(constExpr.getConstType()) && classname.equals(constExpr.getValue()) ||
classType.equals(constExpr.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 varExpr = (VarExprent)expr;
if (varExpr.isDefinition()) {
VarType varType = varExpr.getVarType();
if (classType.equals(varType) || (varType.arrayDim > 0 && classType.value.equals(varType.value))) {
res = true;
}
}
}
if (res) {
return true;
}
}
return false;
}
private static class VarFieldPair {
public String fieldKey;
public VarVersionPair varPair;
VarFieldPair(String field, VarVersionPair varPair) {
this.fieldKey = field;
this.varPair = varPair;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof VarFieldPair)) return false;
VarFieldPair pair = (VarFieldPair)o;
return fieldKey.equals(pair.fieldKey) && varPair.equals(pair.varPair);
}
@Override
public int hashCode() {
return fieldKey.hashCode() + varPair.hashCode();
}
}
}