private static void computeLocalVarsAndDefinitions()

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;
      }
    }
  }