blob: c978395b08390a9b6b598b1a11e331048d9fb071 [file] [log] [blame]
/*
* Copyright 2009 Google Inc.
*
* 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.
*/
package com.google.gwt.dev.jjs.impl.gflow;
import com.google.gwt.dev.jjs.impl.gflow.TransformationFunction.Transformation;
import com.google.gwt.dev.util.Preconditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
/**
* A solver to solve all kinds of analyses defined in the package.
* Uses iterative worklist algorithm.
*
* Solver might be forward or backwards working. Both directions will always
* produce a valid fixed point, which depends on direction. As a rule,
* forward analysis benefits from forward direction, backwards - from the
* opposite.
*
* @param <N> graph node type.
* @param <E> graph edge type.
* @param <T> graph transformer type.
* @param <G> graph type.
* @param <A> assumption type.
*/
public class AnalysisSolver<N, E, T, G extends Graph<N, E, T>,
A extends Assumption<A>> {
/**
* Adapter from IntegratedFlowFunction to FlowFunction. If integrated function
* decides to perform transformation, replacement graph is recursively
* analyzed and result return without actually performing transformation,
*/
private final class IntegratedFlowFunctionAdapter
implements FlowFunction<N, E, G, A> {
private IntegratedFlowFunction<N, E, T, G, A> flowFunction;
private IntegratedFlowFunctionAdapter(
IntegratedAnalysis<N, E, T, G, A> analysis) {
flowFunction = analysis.getIntegratedFlowFunction();
}
public void interpret(final N node, G graph,
final AssumptionMap<E, A> assumptionMap) {
final boolean[] mapWasModified = new boolean[1];
Transformation<T, G> transformation = flowFunction.interpretOrReplace(
node, graph, new AssumptionMap<E, A>() {
public A getAssumption(E edge) {
return assumptionMap.getAssumption(edge);
}
public void setAssumption(E edge, A assumption) {
mapWasModified[0] = true;
assumptionMap.setAssumption(edge, assumption);
}
});
if (transformation == null) {
return;
}
Preconditions.checkArgument(!mapWasModified[0]);
final G newSubgraph = transformation.getNewSubgraph();
if (debug) {
System.err.println("Applying transformation: " + transformation);
System.err.println("Replacing");
System.err.println(node);
System.err.println("With graph:");
System.err.println(newSubgraph);
}
final List<E> inEdges = graph.getInEdges(node);
final List<E> outEdges = graph.getOutEdges(node);
Preconditions.checkArgument(newSubgraph.getGraphInEdges().size() ==
inEdges.size());
Preconditions.checkArgument(newSubgraph.getGraphOutEdges().size() ==
outEdges.size());
iterate(newSubgraph,
new IntegratedAnalysis<N, E, T, G, A>() {
public IntegratedFlowFunction<N, E, T, G, A>
getIntegratedFlowFunction() {
return flowFunction;
}
public void setInitialGraphAssumptions(G graph,
AssumptionMap<E, A> newAssumptionMap) {
for (int i = 0; i < inEdges.size(); ++i) {
newAssumptionMap.setAssumption(newSubgraph.getGraphInEdges().get(i),
assumptionMap.getAssumption(inEdges.get(i)));
}
for (int i = 0; i < outEdges.size(); ++i) {
newAssumptionMap.setAssumption(newSubgraph.getGraphOutEdges().get(i),
assumptionMap.getAssumption(outEdges.get(i)));
}
}
});
for (int i = 0; i < inEdges.size(); ++i) {
assumptionMap.setAssumption(inEdges.get(i),
getEdgeAssumption(newSubgraph, newSubgraph.getGraphInEdges().get(i)));
}
for (int i = 0; i < outEdges.size(); ++i) {
assumptionMap.setAssumption(outEdges.get(i),
getEdgeAssumption(newSubgraph, newSubgraph.getGraphOutEdges().get(i)));
}
}
}
public static boolean debug = false;
/**
* Solve a non-integrated analysis.
*
* @param <N> graph node type.
* @param <E> graph edge type.
* @param <T> graph transformer type.
* @param <G> graph type.
* @param <A> assumption type.
*/
public static <N, E, T, G extends Graph<N, E, T>, A extends Assumption<A>>
Map<E, A> solve(G g, Analysis<N, E, G, A> analysis, boolean forward) {
return new AnalysisSolver<N, E, T, G, A>(forward).solve(g, analysis);
}
/**
* Solve a integrated analysis.
*
* @param <N> graph node type.
* @param <E> graph edge type.
* @param <T> graph transformer type.
* @param <G> graph type.
* @param <A> assumption type.
*/
public static <N, E, T, G extends Graph<N, E, T>, A extends Assumption<A>>
boolean solveIntegrated(G g, IntegratedAnalysis<N, E, T, G, A> analysis,
boolean forward) {
return new AnalysisSolver<N, E, T, G, A>(forward).solveIntegrated(g,
analysis);
}
/**
* If <code>true</code>, then we are moving forward. Moving backwards
* otherwise.
*/
private final boolean forward;
/**
* @param forward <code>true</code> if solvers moves forward.
*/
private AnalysisSolver(boolean forward) {
this.forward = forward;
}
/**
* Apply all transformations based on a found fixed point.
*/
private boolean actualize(G graph,
final IntegratedAnalysis<N, E, T, G, A> analysis) {
TransformationFunction<N, E, T, G, A> function =
new TransformationFunction<N, E, T, G, A>() {
public Transformation<T, G> transform(final N node, final G graph,
AssumptionMap<E, A> assumptionMap) {
final boolean[] didAssumptionChange = new boolean[1];
Transformation<T, G> transformation = analysis.getIntegratedFlowFunction().interpretOrReplace(
node, graph, new AssumptionMap<E, A>() {
public A getAssumption(E edge) {
Preconditions.checkArgument(graph.getStart(edge) == node
|| graph.getEnd(edge) == node);
return getEdgeAssumption(graph, edge);
}
public void setAssumption(E edge, A assumption) {
Preconditions.checkArgument(graph.getStart(edge) == node
|| graph.getEnd(edge) == node);
didAssumptionChange[0] = true;
}
});
Preconditions.checkArgument(transformation == null ||
!didAssumptionChange[0]);
return transformation;
}
};
return applyTransformation(graph, function);
}
private boolean applyTransformation(final G graph,
TransformationFunction<N, E, T, G, A> transformationFunction) {
boolean didChange = false;
for (final N node : graph.getNodes()) {
Transformation<T, G> transformation = transformationFunction.transform(
node, graph, new AssumptionMap<E, A>() {
public A getAssumption(E edge) {
Preconditions.checkArgument(graph.getStart(edge) == node
|| graph.getEnd(edge) == node);
return getEdgeAssumption(graph, edge);
}
public void setAssumption(E edge, A assumption) {
throw new IllegalStateException(
"Transformations should not change assumptions");
}
});
if (transformation != null) {
T actualizer = transformation.getGraphTransformer();
Preconditions.checkNotNull(actualizer, "Null actualizer from: %s",
transformationFunction);
didChange = graph.transform(node, actualizer) || didChange;
}
}
return didChange;
}
private LinkedHashSet<N> buildInitialWorklist(G g) {
ArrayList<N> nodes = new ArrayList<N>(g.getNodes());
LinkedHashSet<N> worklist = new LinkedHashSet<N>(nodes.size());
if (!forward) {
Collections.reverse(nodes);
}
worklist.addAll(nodes);
return worklist;
}
@SuppressWarnings("unchecked")
private A getEdgeAssumption(G graph, E edge) {
return (A) graph.getEdgeData(edge);
}
private void initGraphAssumptions(Analysis<N, E, G, A> analysis, final G graph) {
analysis.setInitialGraphAssumptions(graph, new AssumptionMap<E, A>() {
public A getAssumption(E edge) {
return getEdgeAssumption(graph, edge);
}
public void setAssumption(E edge, A assumption) {
setEdgeAssumption(graph, edge, assumption);
}
});
}
/**
* Find a fixed point of integrated analysis by wrapping it with
* IntegratedFlowFunctionAdapter and calling
* {@link #solveImpl(Graph, Analysis)}.
*/
private void iterate(G graph,
final IntegratedAnalysis<N, E, T, G, A> integratedAnalysis) {
if (debug) {
System.err.println("-----------------------------------------");
System.err.println("Iterate started on:");
System.err.println(graph);
System.err.println("-----------------------------------------");
}
final IntegratedFlowFunctionAdapter adapter =
new IntegratedFlowFunctionAdapter(integratedAnalysis);
Analysis<N, E, G, A> analysis = new Analysis<N, E, G, A>() {
public FlowFunction<N, E, G, A> getFlowFunction() {
return adapter;
}
public void setInitialGraphAssumptions(G graph,
AssumptionMap<E, A> assumptionMap) {
integratedAnalysis.setInitialGraphAssumptions(graph, assumptionMap);
}
};
solveImpl(graph, analysis);
}
private void resetEdgeData(G graph) {
for (N node : graph.getNodes()) {
for (E e : graph.getInEdges(node)) {
graph.setEdgeData(e, null);
}
for (E e : graph.getOutEdges(node)) {
graph.setEdgeData(e, null);
}
}
for (E e : graph.getGraphOutEdges()) {
graph.setEdgeData(e, null);
}
for (E e : graph.getGraphInEdges()) {
graph.setEdgeData(e, null);
}
}
private void setEdgeAssumption(G graph, E edge, A assumption) {
graph.setEdgeData(edge, assumption);
}
/**
* Solve a non-integrated analysis.
*/
private Map<E, A> solve(G g, Analysis<N, E, G, A> analysis) {
solveImpl(g, analysis);
Map<E, A> result = new HashMap<E, A>();
for (N n : g.getNodes()) {
for (E e : g.getInEdges(n)) {
result.put(e, getEdgeAssumption(g, e));
}
for (E e : g.getOutEdges(n)) {
result.put(e, getEdgeAssumption(g, e));
}
}
for (E e : g.getGraphInEdges()) {
result.put(e, getEdgeAssumption(g, e));
}
for (E e : g.getGraphOutEdges()) {
result.put(e, getEdgeAssumption(g, e));
}
return result;
}
/**
* Solve a non-integrated analysis.
*/
private void solveImpl(final G graph, Analysis<N, E, G, A> analysis) {
FlowFunction<N, E, G, A> flowFunction = analysis.getFlowFunction();
final LinkedHashSet<N> worklist = buildInitialWorklist(graph);
resetEdgeData(graph);
initGraphAssumptions(analysis, graph);
while (!worklist.isEmpty()) {
Iterator<N> iterator = worklist.iterator();
final N node = iterator.next();
iterator.remove();
flowFunction.interpret(node, graph, new AssumptionMap<E, A>() {
public A getAssumption(E edge) {
Preconditions.checkArgument(graph.getStart(edge) == node
|| graph.getEnd(edge) == node);
return getEdgeAssumption(graph, edge);
}
public void setAssumption(E edge, A assumption) {
N start = graph.getStart(edge);
N end = graph.getEnd(edge);
Preconditions.checkArgument(start == node || end == node);
if (!AssumptionUtil.equals(getEdgeAssumption(graph, edge), assumption)) {
setEdgeAssumption(graph, edge, assumption);
if (start == node) {
if (end != null) {
worklist.add(end);
}
} else if (end == node) {
if (start != null) {
worklist.add(start);
}
} else {
throw new IllegalStateException();
}
}
}
});
}
}
/**
* Solve an integrated analysis.
*
* Finds a fixed point by using an IntegratedFlowFunctionAdapter and
* recursing into {@link #solve(Graph, Analysis)}. Applies analysis
* transformations based on the found fixed point.
*/
private boolean solveIntegrated(G g, IntegratedAnalysis<N, E, T, G, A> analysis) {
iterate(g, analysis);
return actualize(g, analysis);
}
}