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

org.sonar.python.cfg.fixpoint.LiveVariablesAnalysis Maven / Gradle / Ivy

The newest version!
/*
 * SonarQube Python Plugin
 * Copyright (C) 2011-2024 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Sonar Source-Available License Version 1, as published by SonarSource SA.
 *
 * 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 Sonar Source-Available License for more details.
 *
 * You should have received a copy of the Sonar Source-Available License
 * along with this program; if not, see https://sonarsource.com/license/ssal/
 */
package org.sonar.python.cfg.fixpoint;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.sonar.plugins.python.api.cfg.CfgBlock;
import org.sonar.plugins.python.api.cfg.ControlFlowGraph;
import org.sonar.python.cfg.fixpoint.ReadWriteVisitor.SymbolReadWrite;
import org.sonar.plugins.python.api.symbols.Symbol;

public class LiveVariablesAnalysis {

  private final Map liveVariablesPerBlock = new HashMap<>();

  public static LiveVariablesAnalysis analyze(ControlFlowGraph cfg) {
    LiveVariablesAnalysis instance = new LiveVariablesAnalysis();
    instance.compute(cfg);
    return instance;
  }

  /**
   * See "worklist algorithm" in http://www.cs.cornell.edu/courses/cs4120/2013fa/lectures/lec26-fa13.pdf
   * An alternative terminology for "kill/gen" is "def/use"
   */
  private void compute(ControlFlowGraph cfg) {
    cfg.blocks().forEach(block -> liveVariablesPerBlock.put(block, LiveVariables.build(block)));
    Deque workList = new ArrayDeque<>(cfg.blocks());
    while (!workList.isEmpty()) {
      CfgBlock currentBlock = workList.pop();
      LiveVariables liveVariables = liveVariablesPerBlock.get(currentBlock);
      boolean liveInHasChanged = liveVariables.propagate(liveVariablesPerBlock);
      if (liveInHasChanged) {
        currentBlock.predecessors().forEach(workList::push);
      }
    }
  }

  public LiveVariables getLiveVariables(CfgBlock block) {
    return liveVariablesPerBlock.get(block);
  }

  public Set getReadSymbols() {
    Set readAtLeastOnce = new HashSet<>();
    for (LiveVariables liveVariables : liveVariablesPerBlock.values()) {
      for (Map symbolVariableUsageMap : liveVariables.variableReadWritesPerElement.values()) {
        for (Map.Entry symbolWithUsage : symbolVariableUsageMap.entrySet()) {
          if (symbolWithUsage.getValue().isRead()) {
            readAtLeastOnce.add(symbolWithUsage.getKey());
          }
        }
      }
    }
    return readAtLeastOnce;
  }

  public static class LiveVariables extends CfgBlockState {
    /**
     * The Live-In variables are variables which has values that:
     * - are needed by this block
     * OR
     * - are needed by a successor block and are not killed in this block.
     */
    private Set in = new HashSet<>();

    /**
     * The Live-Out variables are variables which are needed by successors.
     */
    private Set out = new HashSet<>();

    private LiveVariables(CfgBlock block) {
      super(block);
    }

    /**
     * Builds a new LiveVariables instance for the given block and initializes the 'kill' and 'gen' symbol sets.
     */
    public static LiveVariables build(CfgBlock block) {
      LiveVariables instance = new LiveVariables(block);
      instance.init(block);
      return instance;
    }

    /**
     * Propagates backwards: first computes the 'out' set, then the 'in' set.
     */
    private boolean propagate(Map liveVariablesPerBlock) {
      out.clear();
      block.successors().stream()
        .map(liveVariablesPerBlock::get)
        .map(LiveVariables::getIn)
        .forEach(out::addAll);
      // in = gen + (out - kill)
      Set newIn = new HashSet<>(gen);
      newIn.addAll(difference(out, kill));
      boolean inHasChanged = !newIn.equals(in);
      in = newIn;
      return inHasChanged;
    }

    private static Set difference(Set out, Set kill) {
      Set result = new HashSet<>(out);
      result.removeIf(kill::contains);
      return result;
    }


    public Set getIn() {
      return in;
    }

    public Set getOut() {
      return out;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy