in src/org/jetbrains/java/decompiler/main/rels/NestedClassProcessor.java [258:431]
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
DotExporter.toDotFile(method.getOrBuildGraph(), method.methodStruct, "computeLocalVars");
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.getParameters().get(i);
VarFieldPair pair = null;
if (param.type == Exprent.EXPRENT_VAR && mask.get(i) != null) {
VarVersion varPair = new VarVersion((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 (!Objects.equals(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);
var wrapper = nestedNode.getWrapper().getMethodWrapper(CodeConstants.INIT_NAME, entry.getKey());
List<VarVersion> mask = new ArrayList<>(entry.getValue().size());
var attr = wrapper.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_METHOD_PARAMETERS);
if (attr != null) {
for (var param : attr.getEntries()) {
mask.add((param.myAccessFlags & (CodeConstants.ACC_SYNTHETIC | CodeConstants.ACC_MANDATED)) == 0 &&
!groovyClosure(nestedNode) ? null : new VarVersion(-1, 0));
}
} else {
for (VarFieldPair pair : entry.getValue()) {
VarVersion ver = pair != null && !pair.fieldKey.isEmpty() ? pair.varPair : null;
if (ver == null && mask.isEmpty() &&
nestedNode.type == ClassNode.CLASS_MEMBER && !(nestedNode.classStruct.hasModifier(CodeConstants.ACC_STATIC)) &&
(nestedNode.classStruct.getAccessFlags() & CodeConstants.ACC_ENUM) == 0 && //!enum
!groovyClosure(nestedNode)) {
ver = new VarVersion(-1, 0); // non-static inners always have 'Outer.this'
}
mask.add(ver);
}
}
wrapper.synthParameters = mask;
}
}
}