001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.js;
017:
018: import com.google.gwt.dev.jjs.InternalCompilerException;
019: import com.google.gwt.dev.js.ast.JsContext;
020: import com.google.gwt.dev.js.ast.JsExprStmt;
021: import com.google.gwt.dev.js.ast.JsExpression;
022: import com.google.gwt.dev.js.ast.JsFunction;
023: import com.google.gwt.dev.js.ast.JsModVisitor;
024: import com.google.gwt.dev.js.ast.JsName;
025: import com.google.gwt.dev.js.ast.JsNameRef;
026: import com.google.gwt.dev.js.ast.JsProgram;
027: import com.google.gwt.dev.js.ast.JsStatement;
028: import com.google.gwt.dev.js.ast.JsVisitor;
029:
030: import java.util.HashMap;
031: import java.util.Map;
032:
033: /**
034: * Removes JsFunctions that are never referenced in the program.
035: */
036: public class JsUnusedFunctionRemover {
037:
038: /**
039: * Finds all functions in the program.
040: */
041: private class JsFunctionVisitor extends JsVisitor {
042:
043: @Override
044: public void endVisit(JsFunction x, JsContext<JsExpression> ctx) {
045: // Anonymous function, ignore it
046: if (x.getName() != null) {
047: toRemove.put(x.getName(), x);
048: }
049: }
050: }
051:
052: /**
053: * Finds all function references in the program.
054: */
055: private class JsNameRefVisitor extends JsVisitor {
056:
057: @Override
058: public void endVisit(JsNameRef x, JsContext<JsExpression> ctx) {
059: toRemove.remove(x.getName());
060: }
061: }
062:
063: private class RemovalVisitor extends JsModVisitor {
064:
065: @Override
066: public void endVisit(JsExprStmt x, JsContext<JsStatement> ctx) {
067: if (!(x.getExpression() instanceof JsFunction)) {
068: return;
069: }
070:
071: JsFunction f = (JsFunction) x.getExpression();
072:
073: if (!f.isFromJava()) {
074: // This function didn't come from Java source, so we'll assume it's
075: // magic and ignore it.
076: return;
077: }
078:
079: JsName name = f.getName();
080:
081: if (toRemove.containsKey(name)) {
082: // Removing a static initializer indicates a problem in
083: // JsInliner.
084: if (name.getIdent().equals("$clinit")) {
085: throw new InternalCompilerException(
086: "Tried to remove clinit "
087: + name.getStaticRef().toSource());
088: }
089:
090: // Remove the statement
091: ctx.removeMe();
092: }
093: }
094: }
095:
096: public static boolean exec(JsProgram program) {
097: return (new JsUnusedFunctionRemover(program)).execImpl();
098: }
099:
100: private final Map<JsName, JsFunction> toRemove = new HashMap<JsName, JsFunction>();
101: private final JsProgram program;
102:
103: public JsUnusedFunctionRemover(JsProgram program) {
104: this .program = program;
105: }
106:
107: public boolean execImpl() {
108: // Find all functions
109: (new JsFunctionVisitor()).accept(program);
110:
111: // Remove the functions that are referenced from the hit list
112: (new JsNameRefVisitor()).accept(program);
113:
114: // Remove the unused functions from the JsProgram
115: RemovalVisitor removalVisitor = new RemovalVisitor();
116: removalVisitor.accept(program);
117:
118: return removalVisitor.didChange();
119: }
120: }
|