/* * Copyright 2007 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.js; import com.google.gwt.dev.jjs.InternalCompilerException; import com.google.gwt.dev.jjs.impl.OptimizerStats; import com.google.gwt.dev.js.ast.JsContext; import com.google.gwt.dev.js.ast.JsExprStmt; import com.google.gwt.dev.js.ast.JsFunction; import com.google.gwt.dev.js.ast.JsModVisitor; import com.google.gwt.dev.js.ast.JsName; import com.google.gwt.dev.js.ast.JsNameRef; import com.google.gwt.dev.js.ast.JsProgram; import com.google.gwt.dev.js.ast.JsVisitor; import com.google.gwt.dev.util.collect.IdentityHashSet; import com.google.gwt.dev.util.log.speedtracer.CompilerEventType; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger; import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event; import java.util.Set; /** * Removes JsFunctions that are never referenced in the program. */ public class JsUnusedFunctionRemover { private class RemovalVisitor extends JsModVisitor { @Override public void endVisit(JsExprStmt x, JsContext ctx) { if (!(x.getExpression() instanceof JsFunction)) { return; } JsFunction f = (JsFunction) x.getExpression(); JsName name = f.getName(); // Anonymous function, ignore it if (name == null || seen.contains(name) || f.isArtificiallyRescued()) { return; } // Removing a static initializer indicates a problem in JsInliner. if (f.getExecuteOnce()) { throw new InternalCompilerException("Tried to remove clinit " + name.getStaticRef().toSource()); } // Remove the statement ctx.removeMe(); } } /** * Finds all function references in the program. */ private class RescueVisitor extends JsVisitor { @Override public void endVisit(JsNameRef x, JsContext ctx) { seen.add(x.getName()); } } public static final String NAME = JsUnusedFunctionRemover.class.getSimpleName(); public static OptimizerStats exec(JsProgram program) { Event optimizeJsEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE_JS, "optimizer", NAME); OptimizerStats stats = new JsUnusedFunctionRemover(program).execImpl(); optimizeJsEvent.end("didChange", "" + stats.didChange()); return stats; } private final JsProgram program; private final Set<JsName> seen = new IdentityHashSet<JsName>(); public JsUnusedFunctionRemover(JsProgram program) { this.program = program; } public OptimizerStats execImpl() { OptimizerStats stats = new OptimizerStats(NAME); // Rescue all referenced functions. new RescueVisitor().accept(program); // Remove the unused functions from the JsProgram RemovalVisitor removalVisitor = new RemovalVisitor(); removalVisitor.accept(program); if (removalVisitor.didChange()) { stats.recordModified(); } return stats; } }