/* * 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.JConstructor; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.ast.JModVisitor; import com.google.gwt.dev.jjs.ast.JProgram; import com.google.gwt.dev.jjs.ast.JVariable; import java.util.Collection; /** * A visitor for optimizing an AST. * <p> * Subclasses of JChangeTrackingVisitor should override enter/exit instead of visit/endVisit for * JMethod, JConstructor, JVariable and JField * <p> * Passes that modify the Java AST while an OptimizerContext is in effect should subclass this * class (JChangeTrackingVisitor) instead of {@link JModVisitor} or interact with * {@link OptimizerContext} directly. * <p> * Passes that would like to act only on parts of the tree related to the modification that happened * since the pass was last run should do something like: * {@code * String passName = "SomeUniqueNameForThisPass"; * int lastSeenOptmizationStep = optimizerContext.getLastStepFor(passName); * Set<JMethod> modifiedMethods = * optimizerContext.getModifiedMethodsSince(lastSeenOptmizationStep) * Set<JField> modifiedFields = * optimizerContext.getModifiedFieldsSince(lastSeenOptmizationStep) * // compute potentially affected fields and methods and run the optimizer just on those * .... * // * optimizerContext.setLastStepFor(passName, optimizerContext.getCurrentOptimizationStep()); * optimizerContext.incOptimizationStep; * } */ public abstract class JChangeTrackingVisitor extends JModVisitor { private JField currentField; private JMethod currentMethod; private final OptimizerContext optimizerCtx; private boolean fieldModified = false; private boolean methodModified = false; public JChangeTrackingVisitor(OptimizerContext optimizerCtx) { this.optimizerCtx = optimizerCtx; } @Override public final void endVisit(JConstructor x, Context ctx) { exit(x, ctx); if (methodModified) { optimizerCtx.markModified(x); } currentMethod = null; } @Override public final void endVisit(JField x, Context ctx) { exit(x, ctx); if (fieldModified) { optimizerCtx.markModified(x); } currentField = null; } @Override public final void endVisit(JMethod x, Context ctx) { exit(x, ctx); currentMethod = null; if (!methodModified) { return; } optimizerCtx.markModified(x); if (JProgram.isClinit(x) || JProgram.isInit(x)) { // Mark all class static fields as modified when a class clinit is modified to reflect // that when inline declaration statements are modified the corresponding field must // be considered modified. for (JField potentiallyModifiedField: x.getEnclosingType().getFields()) { if (potentiallyModifiedField.isStatic() && JProgram.isClinit(x) || !potentiallyModifiedField.isStatic() && !JProgram.isClinit(x)) { optimizerCtx.markModified(potentiallyModifiedField); } } } } @Override public final void endVisit(JVariable x, Context ctx) { exit(x, ctx); } public boolean enter(JConstructor x, Context ctx) { return enter((JMethod) x, ctx); } public boolean enter(JField x, Context ctx) { return enter((JVariable) x, ctx); } public boolean enter(JMethod x, Context ctx) { return true; } public boolean enter(JVariable x, Context ctx) { return true; } public void exit(JConstructor x, Context ctx) { exit((JMethod) x, ctx); } public void exit(JField x, Context ctx) { exit((JVariable) x, ctx); } public void exit(JMethod x, Context ctx) { return; } public void exit(JVariable x, Context ctx) { return; } public JField getCurrentField() { return currentField; } public JMethod getCurrentMethod() { return currentMethod; } @Override public final boolean visit(JConstructor x, Context ctx) { currentMethod = x; methodModified = false; return enter(x, ctx); } @Override public final boolean visit(JField x, Context ctx) { currentField = x; fieldModified = false; return enter(x, ctx); } @Override public final boolean visit(JMethod x, Context ctx) { currentMethod = x; methodModified = false; return enter(x, ctx); } @Override public final boolean visit(JVariable x, Context ctx) { return enter(x, ctx); } public final void wasRemoved(JField field) { optimizerCtx.remove(field); } public final void wasRemoved(JMethod method) { optimizerCtx.remove(method); } public final void methodsWereRemoved(Collection<JMethod> methods) { optimizerCtx.removeMethods(methods); } public final void fieldsWereRemoved(Collection<JField> fields) { optimizerCtx.removeFields(fields); } @Override protected final void madeChanges() { super.madeChanges(); if (currentMethod != null) { methodModified = true; } if (currentField != null) { fieldModified = true; } } }