All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.java.cfg.LiveVariables Maven / Gradle / Ivy

/*
 * SonarQube Java
 * Copyright (C) 2012 SonarSource
 * [email protected]
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.java.cfg;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.sonar.plugins.java.api.semantic.Symbol;
import org.sonar.plugins.java.api.tree.AssignmentExpressionTree;
import org.sonar.plugins.java.api.tree.ExpressionTree;
import org.sonar.plugins.java.api.tree.IdentifierTree;
import org.sonar.plugins.java.api.tree.LambdaExpressionTree;
import org.sonar.plugins.java.api.tree.NewClassTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.VariableTree;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class
    LiveVariables {

  private final CFG cfg;
  private final Map> out = new HashMap<>();

  private LiveVariables(CFG cfg) {
    this.cfg = cfg;
  }

  public Set getOut(CFG.Block block) {
    return out.get(block);
  }

  public static LiveVariables analyze(CFG cfg) {
    LiveVariables liveVariables = new LiveVariables(cfg);
    final Map> in = new HashMap<>();
    // Generate kill/gen for each block in isolation
    Map> kill = new HashMap<>();
    Map> gen = new HashMap<>();
    for (CFG.Block block : liveVariables.cfg.reversedBlocks()) {
      Set blockKill = new HashSet<>();
      Set blockGen = new HashSet<>();
      liveVariables.processBlockElements(block, blockKill, blockGen);
      kill.put(block, blockKill);
      gen.put(block, blockGen);
    }
    liveVariables.analyzeCFG(in, kill, gen);
    // out of exit block are empty by definition.
    if (!liveVariables.out.get(liveVariables.cfg.reversedBlocks().get(0)).isEmpty()) {
      throw new IllegalStateException("Out of exit block should be empty");
    }

    // Make things immutable.
    for (Map.Entry> blockSetEntry : liveVariables.out.entrySet()) {
      blockSetEntry.setValue(ImmutableSet.copyOf(blockSetEntry.getValue()));
    }

    return liveVariables;
  }

  private void analyzeCFG(Map> in, Map> kill, Map> gen) {
    Deque workList = new LinkedList<>();
    workList.addAll(cfg.reversedBlocks());
    while (!workList.isEmpty()) {
      CFG.Block block = workList.removeFirst();

      Set blockOut = out.get(block);
      if (blockOut == null) {
        blockOut = new HashSet<>();
        out.put(block, blockOut);
      }
      for (CFG.Block successor : block.successors()) {
        Set inOfSuccessor = in.get(successor);
        if (inOfSuccessor != null) {
          blockOut.addAll(inOfSuccessor);
        }
      }
      // in = gen and (out - kill)
      Set newIn = new HashSet<>(gen.get(block));
      newIn.addAll(Sets.difference(blockOut, kill.get(block)));

      if (newIn.equals(in.get(block))) {
        continue;
      }
      in.put(block, newIn);
      for (CFG.Block predecessor : block.predecessors()) {
        workList.addLast(predecessor);
      }
    }
  }

  private void processBlockElements(CFG.Block block, Set blockKill, Set blockGen) {
    // process elements from bottom to top
    Set assignmentLHS = new HashSet<>();
    for (Tree element : Lists.reverse(block.elements())) {
      Symbol symbol;
      switch (element.kind()) {
        case ASSIGNMENT:
          ExpressionTree lhs = ((AssignmentExpressionTree) element).variable();
          if (lhs.is(Tree.Kind.IDENTIFIER)) {
            symbol = ((IdentifierTree) lhs).symbol();
            if (isLocalVariable(symbol)) {
              assignmentLHS.add(lhs);
              blockGen.remove(symbol);
              blockKill.add(symbol);
            }
          }
          break;
        case IDENTIFIER:
          symbol = ((IdentifierTree) element).symbol();
          if (!assignmentLHS.contains(element) && isLocalVariable(symbol)) {
            blockGen.add(symbol);
          }
          break;
        case VARIABLE:
          blockKill.add(((VariableTree) element).symbol());
          blockGen.remove(((VariableTree) element).symbol());
          break;
        case LAMBDA_EXPRESSION:
          blockGen.addAll(getUsedVariables(((LambdaExpressionTree) element).body(), cfg.methodSymbol()));
          break;
        case NEW_CLASS:
          blockGen.addAll(getUsedVariables(((NewClassTree) element).classBody(), cfg.methodSymbol()));
          break;
        default:
          // Ignore other kind of elements, no change of gen/kill
      }
    }
  }

  private static boolean isLocalVariable(Symbol symbol) {
    return symbol.owner().isMethodSymbol();
  }

  private static List getUsedVariables(@Nullable Tree syntaxNode, Symbol.MethodSymbol owner) {
    if(syntaxNode == null) {
      return Collections.emptyList();
    }
    LocalVariableReadExtractor extractorFromClass = new LocalVariableReadExtractor(owner);
    syntaxNode.accept(extractorFromClass);
    return extractorFromClass.usedVariables();
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy