blob: 3512e3e743d14432e2a46dc8b85a6b3c559bec5c [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.IdentityHashMap;
import java.util.List;
import java.util.Map;
/**
* Integrated analysis, which combines several other integrated analyses into
* one. It does so be defining combined assumption, which is vector of original
* assumptions, and applies each analysis to its own component.
*
* If any analysis decides to rewrite the node, combined analysis returns
* produced transformation. If more than one analyses decide to transform, the
* first one wins.
*
* @param <N> graph node type.
* @param <E> graph edge type.
* @param <T> graph transformer type.
* @param <G> graph type.
*
*/
public class CombinedIntegratedAnalysis<N, E, T, G extends Graph<N, E, T>>
implements
IntegratedAnalysis<N, E, T, G, CombinedIntegratedAnalysis.CombinedAssumption> {
/**
* Combined assumption which holds vector of original assumptions.
*/
public static class CombinedAssumption implements
Assumption<CombinedAssumption> {
private static class CopyOnWrite {
private final int size;
private CombinedAssumption assumption;
private boolean copied = false;
private CopyOnWrite(CombinedAssumption assumption, int size) {
this.assumption = assumption;
this.size = size;
}
public boolean isCopied() {
return copied;
}
public void set(int slice, Assumption<?> assumption) {
copyIfNeeded();
this.assumption.set(slice, assumption);
}
public CombinedAssumption unwrap() {
return assumption;
}
private void copyIfNeeded() {
if (!copied) {
copied = true;
if (assumption == null) {
assumption = new CombinedAssumption(size);
} else {
assumption = new CombinedAssumption(assumption);
}
}
}
}
/**
* Individual assumptions vector.
*/
private final List<Assumption<?>> assumptions;
public CombinedAssumption(CombinedAssumption assumption) {
this.assumptions = new ArrayList<Assumption<?>>(assumption.assumptions);
}
public CombinedAssumption(int size) {
this.assumptions = new ArrayList<Assumption<?>>(size);
for (int i = 0; i < size; ++i) {
this.assumptions.add(null);
}
}
public CombinedAssumption(List<Assumption<?>> assumptions) {
this.assumptions = assumptions;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
CombinedAssumption other = (CombinedAssumption) obj;
// We do not implement equals in our zipped lists. Do it here.
if (other.assumptions.size() != assumptions.size()) {
return false;
}
for (int i = 0; i < assumptions.size(); ++i) {
Assumption<?> a1 = assumptions.get(i);
Assumption<?> a2 = other.assumptions.get(i);
if (a1 == null) {
if (a1 != a2) {
return false;
}
} else {
if (a2 == null) {
return false;
}
if (!a1.equals(a2)) {
return false;
}
}
}
return true;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((assumptions == null) ? 0 : assumptions.hashCode());
return result;
}
/**
* Joins combined assumption by joining all individual components.
*/
@SuppressWarnings("unchecked")
public CombinedAssumption join(CombinedAssumption value) {
if (value == null) {
return this;
}
Preconditions.checkArgument(value.assumptions.size() ==
assumptions.size());
List<Assumption<?>> newAssumptions = new ArrayList<Assumption<?>>();
for (int i = 0; i < assumptions.size(); ++i) {
Assumption a1 = assumptions.get(i);
Assumption a2 = value.assumptions.get(i);
newAssumptions.add(AssumptionUtil.join(a1, a2));
}
return new CombinedAssumption(newAssumptions);
}
@Override
public String toString() {
return assumptions.toString();
}
/**
* Gets nth assumption component.
*/
private Assumption<?> get(int n) {
return assumptions.get(n);
}
private void set(int slice, Assumption<?> assumption) {
assumptions.set(slice, assumption);
}
}
/**
* Combined integrated flow function.
*/
private final class CombinedIntegratedFlowFunction implements
IntegratedFlowFunction<N, E, T, G, CombinedAssumption> {
@SuppressWarnings("unchecked")
public Transformation interpretOrReplace(final N node, final G graph,
final AssumptionMap<E, CombinedAssumption> assumptionMap) {
final Map<E, CombinedAssumption.CopyOnWrite> newAssumptions = new IdentityHashMap<E, CombinedAssumption.CopyOnWrite>();
final int size = functions.size();
for (int i = 0; i < size; ++i) {
final int slice = i;
IntegratedFlowFunction function = functions.get(i);
Transformation transformation =
function.interpretOrReplace(node, graph,
new AssumptionMap() {
public Assumption getAssumption(Object edge) {
CombinedAssumption combinedAssumption = assumptionMap.getAssumption((E) edge);
if (combinedAssumption == null) {
return null;
}
return combinedAssumption.get(slice);
}
public void setAssumption(Object edge, Assumption assumption) {
CombinedAssumption.CopyOnWrite newAssumption = newAssumptions.get(edge);
if (newAssumption == null) {
newAssumption = new CombinedAssumption.CopyOnWrite(assumptionMap.getAssumption((E) edge), size);
newAssumptions.put((E) edge, newAssumption);
}
newAssumption.set(slice, assumption);
}
@Override
public String toString() {
return AssumptionUtil.toString(graph.getInEdges(node),
graph.getOutEdges(node), this);
}
});
if (transformation != null) {
return transformation;
}
}
for (E e : newAssumptions.keySet()) {
CombinedAssumption.CopyOnWrite newAssumption = newAssumptions.get(e);
if (newAssumption.isCopied()) {
assumptionMap.setAssumption(e, newAssumption.unwrap());
}
}
return null;
}
}
/**
* Factory method.
*/
public static <N, E, T, G extends Graph<N, E, T>>
CombinedIntegratedAnalysis<N, E, T, G> createAnalysis() {
return new CombinedIntegratedAnalysis<N, E, T, G>();
}
/**
* Individual analyses.
*/
List<IntegratedAnalysis<N, E, T, G, ?>> analyses =
new ArrayList<IntegratedAnalysis<N, E, T, G, ?>>();
/**
* Their flow functions.
*/
List<IntegratedFlowFunction<N, E, T, G, ?>> functions =
new ArrayList<IntegratedFlowFunction<N, E, T, G, ?>>();
/**
* Adds analysis to the combined one.
*/
public void addAnalysis(IntegratedAnalysis<N, E, T, G, ?> analysis) {
analyses.add(analysis);
functions.add(analysis.getIntegratedFlowFunction());
}
public IntegratedFlowFunction<N, E, T, G, CombinedAssumption>
getIntegratedFlowFunction() {
return new CombinedIntegratedFlowFunction();
}
@SuppressWarnings("unchecked")
public void setInitialGraphAssumptions(G graph,
final AssumptionMap<E, CombinedAssumption> assumptionMap) {
for (int i = 0; i < functions.size(); ++i) {
final int slice = i;
IntegratedAnalysis<N, E, T, G, ?> analysis = analyses.get(slice);
analysis.setInitialGraphAssumptions(graph, new AssumptionMap() {
public Assumption getAssumption(Object edge) {
throw new UnsupportedOperationException();
}
public void setAssumption(Object edge,Assumption assumption) {
CombinedAssumption combinedAssumption = assumptionMap.getAssumption((E) edge);
if (combinedAssumption == null) {
combinedAssumption = new CombinedAssumption(functions.size());
combinedAssumption.set(slice, assumption);
assumptionMap.setAssumption((E) edge, combinedAssumption);
} else {
combinedAssumption.set(slice, assumption);
}
}
});
}
}
}