/* * Copyright 2014 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; import com.google.gwt.dev.jjs.ast.Context; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JFieldRef; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JProgram; import com.google.gwt.dev.jjs.ast.JVisitor; import com.google.gwt.thirdparty.guava.common.collect.LinkedHashMultimap; import com.google.gwt.thirdparty.guava.common.collect.Multimap; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; /** * FieldReferences Graph, which records {referencedFields <-> methods} pairs. */ public class FieldReferencesGraph { /** * Visitor used to build fieldReferencesGraph. */ class BuildFieldReferencesGraphVisitor extends JVisitor { private JMethod currentMethod; @Override public void endVisit(JFieldRef x, Context ctx) { JField field = x.getField(); if (currentMethod != null) { methodsByReferencedField.put(field, currentMethod); referencedFieldsByMethod.put(currentMethod, field); } } @Override public void endVisit(JMethod x, Context ctx) { assert (currentMethod == x); currentMethod = null; } @Override public boolean visit(JMethod x, Context ctx) { assert (currentMethod == null); currentMethod = x; return true; } } private Multimap<JField, JMethod> methodsByReferencedField = LinkedHashMultimap.create(); private Multimap<JMethod, JField> referencedFieldsByMethod = LinkedHashMultimap.create(); /** * Build the field references graph of a JProgram. */ public void buildFieldReferencesGraph(JProgram program) { reset(); BuildFieldReferencesGraphVisitor buildFieldUsesVisitor = new BuildFieldReferencesGraphVisitor(); buildFieldUsesVisitor.accept(program); } /** * Return the referenced fields by {@code methods}. */ public Set<JField> getReferencedFieldsByMethods(Collection<JMethod> methods) { assert (methods != null); Set<JField> result = new LinkedHashSet<JField>(); for (JMethod method : methods) { result.addAll(referencedFieldsByMethod.get(method)); } return result; } /** * Return the methods that reference {@code fields}. */ public Set<JMethod> getReferencingMethodsForFields(Collection<JField> fields) { assert (fields != null); Set<JMethod> referencingMethods = new LinkedHashSet<JMethod>(); for (JField field : fields) { referencingMethods.addAll(methodsByReferencedField.get(field)); } return referencingMethods; } /** * For removing a field, remove the {referencedFields <-> methods} pairs that are related to the * field. */ public void removeField(JField field) { Collection<JMethod> methods = methodsByReferencedField.removeAll(field); for (JMethod method : methods) { referencedFieldsByMethod.remove(method, field); } } /** * For removing a method, remove the {referencedFields <-> methods} pairs that are related to the * method. */ public void removeMethod(JMethod method) { Collection<JField> referencedFields = referencedFieldsByMethod.removeAll(method); for (JField referencedField : referencedFields) { methodsByReferencedField.remove(referencedField, method); } } /** * Reset the graph. */ public void reset() { methodsByReferencedField.clear(); referencedFieldsByMethod.clear(); } /** * Update field references graph of a method. */ public void updateFieldReferencesOfMethod(JMethod method) { removeMethod(method); BuildFieldReferencesGraphVisitor buildFieldUsesVisitor = new BuildFieldReferencesGraphVisitor(); buildFieldUsesVisitor.accept(method); } }