| /* |
| * 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); |
| } |
| } |
| }); |
| } |
| } |
| } |