/* * Copyright 2008 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.constants; import com.google.gwt.dev.jjs.ast.HasName; import com.google.gwt.dev.jjs.ast.JDoubleLiteral; import com.google.gwt.dev.jjs.ast.JFloatLiteral; import com.google.gwt.dev.jjs.ast.JValueLiteral; import com.google.gwt.dev.jjs.ast.JVariable; import com.google.gwt.dev.jjs.impl.gflow.Assumption; import com.google.gwt.dev.util.Preconditions; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Assumptions for ConstantsAnalysis. * * Is a map from variable into it's constant value. If variable is not present * in the map, then it's not a constant. * * Empty ConstantsAssumption is a top of the lattice, and is not equals to * null assumption (which is the bottom of every lattice). */ public class ConstantsAssumption implements Assumption<ConstantsAssumption> { /** * Updates the assumption by copying it on first write. */ public static class Updater { private ConstantsAssumption assumption; private boolean copied = false; public Updater(ConstantsAssumption assumption) { this.assumption = assumption; } public Updater copy() { return new Updater(assumption); } public boolean hasAssumption(JVariable target) { if (assumption == null) { return false; } return assumption.hasAssumption(target); } public void set(JVariable target, JValueLiteral literal) { copyIfNeeded(); assumption.set(target, literal); } public ConstantsAssumption unwrap() { if (assumption != null && assumption.isEmpty()) { return TOP; } return assumption; } private void copyIfNeeded() { if (!copied) { assumption = new ConstantsAssumption(assumption); copied = true; } } } /** * A wrapper around JValueLiteral to give it equals() method. */ private static class LiteralWrapper { private final JValueLiteral literal; LiteralWrapper(JValueLiteral literal) { Preconditions.checkNotNull(literal); this.literal = literal; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } LiteralWrapper other = (LiteralWrapper) obj; return equal(this.literal, other.literal); } @Override public int hashCode() { throw new UnsupportedOperationException(); } @Override public String toString() { return literal.toString(); } } /** * A TOP for the lattice. Means that all variables are not constant. */ public static ConstantsAssumption TOP = new ConstantsAssumption(); private static boolean equal(JValueLiteral literal1, JValueLiteral literal2) { if (literal1 == null || literal2 == null) { return literal1 == literal2; } if (literal1 == literal2) { return true; } if (literal1.getClass() != literal2.getClass()) { // these are different literal types. return false; } if (literal1 instanceof JFloatLiteral) { int bits1 = Float.floatToRawIntBits( ((JFloatLiteral) literal1).getValue()); int bits2 = Float.floatToRawIntBits( ((JFloatLiteral) literal2).getValue()); return bits1 == bits2; } if (literal1 instanceof JDoubleLiteral) { long bits1 = Double.doubleToRawLongBits( ((JDoubleLiteral) literal1).getValue()); long bits2 = Double.doubleToRawLongBits( ((JDoubleLiteral) literal2).getValue()); return bits1 == bits2; } Object valueObj1 = literal1.getValueObj(); Object valueObj2 = literal2.getValueObj(); if (valueObj1 == null || valueObj2 == null) { return valueObj1 == valueObj2; } return valueObj1.equals(valueObj2); } private static JValueLiteral join(JValueLiteral value1, JValueLiteral value2) { if (!equal(value1, value2)) { return null; } return value1; } private static JValueLiteral join(LiteralWrapper wrapper1, LiteralWrapper wrapper2) { if (wrapper1 == null || wrapper2 == null) { return null; } return join(wrapper1.literal, wrapper2.literal); } /** * Contains individual assumptions about variables. If variable isn't in the * map, then variable assumption is _|_ (bottom), if variable's value is * null, then variable assumption is T - variable has non-constant value. */ private final Map<JVariable, LiteralWrapper> values; public ConstantsAssumption() { values = new HashMap<JVariable, LiteralWrapper>(); } public ConstantsAssumption(ConstantsAssumption a) { if (a != null) { values = new HashMap<JVariable, LiteralWrapper>(a.values); } else { values = new HashMap<JVariable, LiteralWrapper>(); } } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj == null) { return false; } ConstantsAssumption other = (ConstantsAssumption) obj; return values.equals(other.values); } /** * Get variable constant assumption. <code>null</code> if there's no constant * assumption for this variable. */ public JValueLiteral get(JVariable variable) { LiteralWrapper wrapper = values.get(variable); return wrapper != null ? wrapper.literal : null; } /** * Check if we have constant (i.e. not top and not bottom) assumption about * the variable. */ public boolean hasAssumption(JVariable variable) { return get(variable) != null; } @Override public int hashCode() { return values.hashCode(); } public boolean isEmpty() { return values.isEmpty(); } public ConstantsAssumption join(ConstantsAssumption other) { if (other == null) { return this; } if (other == TOP || this == TOP || isEmpty() || other.isEmpty()) { return TOP; } ConstantsAssumption result = new ConstantsAssumption(); for (JVariable var : other.values.keySet()) { if (values.containsKey(var)) { // Var is present in both assumptions. Join their values. JValueLiteral value = join(values.get(var), other.values.get(var)); if (value != null) { result.values.put(var, new LiteralWrapper(value)); } } } if (result.isEmpty()) { return TOP; } return result; } public String toDebugString() { if (this == TOP || isEmpty()) { return "T"; } StringBuffer result = new StringBuffer(); result.append("{"); List<JVariable> variables = new ArrayList<JVariable>(values.keySet()); HasName.Util.sortByName(variables); for (JVariable variable : variables) { if (result.length() > 1) { result.append(", "); } result.append(variable.getName()); result.append(" = "); if (values.get(variable) == null) { result.append("T"); } else { result.append(values.get(variable)); } } result.append("}"); return result.toString(); } @Override public String toString() { return toDebugString(); } void set(JVariable variable, JValueLiteral literal) { if (literal != null) { values.put(variable, new LiteralWrapper(literal)); } else { values.remove(variable); } } }