blob: a8e808788911ec9cd388204d22c6932d7d511373 [file] [log] [blame]
/*
* 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.jjs.impl;
import com.google.gwt.dev.jjs.ast.CanBeSetFinal;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JBinaryOperation;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConstructor;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JField;
import com.google.gwt.dev.jjs.ast.JLocal;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JPostfixOperation;
import com.google.gwt.dev.jjs.ast.JPrefixOperation;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JVariable;
import com.google.gwt.dev.jjs.ast.JVariableRef;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
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.HashSet;
import java.util.Set;
/**
* Finds all items are effectively final. That is, methods that are never
* overridden, classes that are never subclassed, and variables that are never
* reassigned. Mark all such methods and classes as final, since it helps us
* optimize.
*/
public class Finalizer {
private static final String NAME = Finalizer.class.getSimpleName();
/**
* Any items that weren't marked during MarkVisitor can be set final.
*
* Open question: What does it mean if an interface/abstract method becomes
* final? Is this possible after Pruning? I guess it means that someone tried
* to make a call to method that wasn't actually implemented anywhere in the
* program. But if it wasn't implemented, then the enclosing class should have
* come up as not instantiated and been culled. So I think it's not possible.
*/
private class FinalizeVisitor extends JModVisitor {
@Override
public void endVisit(JClassType x, Context ctx) {
if (!x.isFinal() && !isSubclassed.contains(x)) {
setFinal(x);
}
}
@Override
public void endVisit(JConstructor x, Context ctx) {
// Not applicable.
}
@Override
public void endVisit(JField x, Context ctx) {
if (!x.isVolatile()) {
maybeFinalize(x);
}
}
@Override
public void endVisit(JLocal x, Context ctx) {
maybeFinalize(x);
}
@Override
public void endVisit(JMethod x, Context ctx) {
if (!x.isFinal() && !isOverriden.contains(x)) {
setFinal(x);
}
}
@Override
public void endVisit(JParameter x, Context ctx) {
maybeFinalize(x);
}
@Override
public boolean visit(JClassType x, Context ctx) {
// Don't visit external types, because we can't change their final
// specifiers.
return !x.isExternal();
}
@Override
public boolean visit(JMethodBody x, Context ctx) {
for (JLocal local : x.getLocals()) {
maybeFinalize(local);
}
return false;
}
private void maybeFinalize(JVariable x) {
if (!x.isFinal() && !isReassigned.contains(x)) {
setFinal(x);
}
}
private void setFinal(CanBeSetFinal x) {
x.setFinal();
assert x.isFinal();
madeChanges();
}
}
/**
* Find all items that ARE overriden/subclassed/reassigned.
*/
private class MarkVisitor extends JVisitor {
@Override
public void endVisit(JBinaryOperation x, Context ctx) {
if (x.getOp().isAssignment()) {
recordAssignment(x.getLhs());
}
}
@Override
public void endVisit(JClassType x, Context ctx) {
if (x.getSuperClass() != null) {
isSubclassed.add(x.getSuperClass());
}
}
@Override
public void endVisit(JConstructor x, Context ctx) {
// Never overridden.
}
@Override
public void endVisit(JDeclarationStatement x, Context ctx) {
// This is not a reassignment, the target may still be final.
}
@Override
public void endVisit(JMethod x, Context ctx) {
for (int i = 0; i < x.getOverrides().size(); ++i) {
JMethod it = x.getOverrides().get(i);
isOverriden.add(it);
}
}
@Override
public void endVisit(JPostfixOperation x, Context ctx) {
if (x.getOp().isModifying()) {
recordAssignment(x.getArg());
}
}
@Override
public void endVisit(JPrefixOperation x, Context ctx) {
if (x.getOp().isModifying()) {
recordAssignment(x.getArg());
}
}
@Override
public void endVisit(JsniFieldRef x, Context ctx) {
if (x.isLvalue()) {
recordAssignment(x);
}
}
@Override
public boolean visit(JClassType x, Context ctx) {
// Don't visit external types, because we can't change their final
// specifiers.
return !x.isExternal();
}
private void recordAssignment(JExpression lhs) {
if (lhs instanceof JVariableRef) {
JVariableRef variableRef = (JVariableRef) lhs;
isReassigned.add(variableRef.getTarget());
}
}
}
public static OptimizerStats exec(JProgram program) {
Event optimizeEvent = SpeedTracerLogger.start(
CompilerEventType.OPTIMIZE, "optimizer", NAME);
OptimizerStats stats = new Finalizer().execImpl(program);
optimizeEvent.end("didChange", "" + stats.didChange());
return stats;
}
private final Set<JMethod> isOverriden = new HashSet<JMethod>();
private final Set<JVariable> isReassigned = new HashSet<JVariable>();
private final Set<JClassType> isSubclassed = new HashSet<JClassType>();
private Finalizer() {
}
private OptimizerStats execImpl(JProgram program) {
MarkVisitor marker = new MarkVisitor();
marker.accept(program);
FinalizeVisitor finalizer = new FinalizeVisitor();
finalizer.accept(program);
return new OptimizerStats(NAME).recordModified(
finalizer.getNumMods());
}
}