/** * Copyright 2008 Anders Hessellund * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: MonotonicDataFlowFramework.java,v 1.1 2008/01/17 18:48:19 hessellund Exp $ */ package org.ofbiz.plugin.analysis; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.eclipse.jdt.core.dom.Statement; //inspired by source code from a compiler course by Robby abstract class MonotonicDataFlowFramework<E> { protected ControlFlowGraph cfg; protected Map<Statement, Set<E>> outMap = new HashMap<Statement, Set<E>>(); protected Set<E> init; protected Set<E> bottom; protected boolean isForward; /** aka least upper bound/union (true) or greatest upper bound/intersection (false) */ protected boolean meetOperator; protected boolean isDone; /** * Construct a customized data flow analysis * @param cfg * The control flow graph of a method * @param isForward * The direction of analysis, e.g., forward for Reaching Defs and backward for Liveness * @param isLeastUpperBound * The <i>meet</i> operator is either union (true) or intersection (false) */ MonotonicDataFlowFramework(ControlFlowGraph cfg, boolean isForward, boolean meetOperator) { assert cfg != null; this.cfg = cfg; this.isForward = isForward; this.meetOperator = meetOperator; } /** Computes the fix point solution. */ abstract void computeFixPoint(); abstract String toString(E e); abstract String getAnalysisName(); String getResultString() { StringBuilder sb = new StringBuilder("*** " + getAnalysisName() + " for "); sb.append(Util.getFirstLine(cfg.method)); sb.append(" ***\n*** InSet Map ***\n"); sb.append(getResultString(true)); sb.append("\n*** OutSet Map ***\n"); sb.append(getResultString(false)); return sb.toString(); } /** * Returns the in-set of a {@link Statement}. * @param stmt The {@link Statement}. * @return The in-set of the given {@link Statement}. */ Set<E> getInSet(Statement stmt) { assert stmt != null; Set<E> inSet; boolean first = true; if (isForward ? stmt == cfg.start : stmt == cfg.end) { inSet = new HashSet<E>(init); first = false; } else { inSet = new HashSet<E>(); } Set<Statement> set = isForward ? cfg.predecessors.get(stmt) : cfg.successors.get(stmt); if (set == null) { return inSet; } for (Statement predS : set) { if (first) { inSet.addAll(getOutSet(predS)); first = false; } else { if (meetOperator) { inSet.addAll(getOutSet(predS)); } else { inSet.retainAll(getOutSet(predS)); } } } return inSet; } /** * Returns the out-set of a {@link Statement}. * @param stmt The {@link Statement}. * @return The out-set of the given {@link Statement}. */ Set<E> getOutSet(Statement stmt) { assert stmt != null; Set<E> result = outMap.get(stmt); if (result == null) { result = new HashSet<E>(bottom); outMap.put(stmt, result); } return result; } protected String getResultString(boolean isInSet) { StringBuilder sb = new StringBuilder(); ArrayList<String> list = new ArrayList<String>(); for (Statement s : outMap.keySet()) { sb.append("("+s.getStartPosition()+") "); if (s == cfg.end) { sb.append("(VIRTUAL LAST)"); } sb.append(Util.getFirstLine(s)); sb.append("\t==>\t"); TreeSet<String> ts = new TreeSet<String>(); for (E e : (isInSet ? getInSet(s) : getOutSet(s))) { ts.add(toString(e)); } for (String str : ts) { sb.append(str); sb.append(" # "); } String str = sb.toString(); sb.setLength(0); list.add(str.substring(0, str.length() - 5) + "\n"); } Collections.sort(list); for (String s : list) { sb.append(s); } return sb.toString(); } protected void computeFixPoint(Set<E> init) { computeFixPoint(init, new HashSet<E>()); } protected void computeFixPoint(Set<E> init, Set<E> bottom) { this.bottom = bottom; if (isDone) { return; } isDone = true; this.init = init; Set<Statement> seen = new HashSet<Statement>(); while (iterate(seen, isForward ? cfg.start : cfg.end)) { seen.clear(); } } protected boolean iterate(Set<Statement> seen, Statement stmt) { if (seen.contains(stmt)) { return false; } boolean hasChanged = compute(stmt); seen.add(stmt); Set<Statement> succs = isForward ? cfg.successors.get(stmt) : cfg.predecessors.get(stmt); if (succs != null) { for (Statement succS : succs) { hasChanged = iterate(seen, succS) || hasChanged; } } return hasChanged; } protected boolean compute(Statement stmt) { Set<E> inSet = getInSet(stmt); inSet.removeAll(kill(inSet, stmt)); inSet.addAll(gen(inSet, stmt)); Set<E> outSet = getOutSet(stmt); if (outSet.size() != inSet.size() || !outSet.containsAll(inSet) || !inSet.containsAll(outSet)) { outSet.clear(); outSet.addAll(inSet); inSet.clear(); return true; } inSet.clear(); return false; } /** definitions generated by this statement */ protected abstract Set<E> gen(Set<E> set, Statement stmt); /** definitions killed by this statement */ protected abstract Set<E> kill(Set<E> set, Statement stmt); }