blob: 1f0205e62d4d603c53c03e193e754cd8428d8cd3 [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.Context;
import com.google.gwt.dev.jjs.ast.JArrayType;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JExpression;
import com.google.gwt.dev.jjs.ast.JInterfaceType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodCall;
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNewInstance;
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JNullType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JReferenceType;
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;
/**
* Update polymorphic method calls to tighter bindings based on the type of the
* qualifier. For a given polymorphic method call to a non-final target, see if
* the static type of the qualifer would let us target an override instead.
*
* This is possible because the qualifier might have been tightened by
* {@link com.google.gwt.dev.jjs.impl.TypeTightener}.
*/
public class MethodCallTightener {
public static final String NAME = MethodCallTightener.class.getSimpleName();
/**
* Updates polymorphic method calls to tighter bindings based on the type of
* the qualifier.
*/
public class MethodCallTighteningVisitor extends JModVisitor {
@Override
public void endVisit(JNewInstance x, Context ctx) {
// Do not tighten new operations.
}
@Override
public void endVisit(JMethodCall x, Context ctx) {
JMethod method = x.getTarget();
JExpression instance = x.getInstance();
// The method call is already known statically
if (!x.canBePolymorphic()) {
return;
}
JReferenceType instanceType = ((JReferenceType) instance.getType()).getUnderlyingType();
JReferenceType enclosingType = method.getEnclosingType();
if (instanceType == enclosingType
|| instanceType instanceof JInterfaceType) {
// This method call is as tight as it can be for the type of the
// qualifier
return;
}
if (instanceType instanceof JArrayType) {
// shouldn't get here; arrays don't have extra methods
return;
}
if (instanceType instanceof JNullType) {
// TypeTightener will handle this case
return;
}
assert (instanceType instanceof JClassType);
/*
* Search myself and all my super types to find a tighter implementation
* of the called method, if possible.
*/
JMethod foundMethod = null;
JClassType type;
outer : for (type = (JClassType) instanceType; type != null
&& type != enclosingType; type = type.getSuperClass()) {
for (JMethod methodIt : type.getMethods()) {
if (methodOverrides(methodIt, method)) {
foundMethod = methodIt;
break outer;
}
}
}
if (foundMethod == null) {
return;
}
/*
* Replace the call to the original method with a call to the same method
* on the tighter type.
*/
JMethodCall call = new JMethodCall(x.getSourceInfo(), x.getInstance(),
foundMethod);
call.addArgs(x.getArgs());
ctx.replaceMe(call);
}
/**
* Check whether <code>subMethod</code> overrides <code>supMethod</code>.
* For the purposes of this method, indirect overrides are considered
* overrides. For example, if method A.m overrides B.m, and B.m overrides
* C.m, then A.m is considered to override C.m. Additionally, implementing
* an interface is considered
* <q>overriding</q>
* for the purposes of this method.
*
*/
private boolean methodOverrides(JMethod subMethod, JMethod supMethod) {
if (subMethod.getParams().size() != supMethod.getParams().size()) {
// short cut: check the number of parameters
return false;
}
if (!subMethod.getName().equals(supMethod.getName())) {
// short cut: check the method names
return false;
}
// long way: get all overrides and see if supMethod is included
return program.typeOracle.getAllOverrides(subMethod).contains(supMethod);
}
}
public static OptimizerStats exec(JProgram program) {
Event optimizeEvent = SpeedTracerLogger.start(
CompilerEventType.OPTIMIZE, "optimizer", NAME);
OptimizerStats stats = new MethodCallTightener(program).execImpl();
optimizeEvent.end("didChange", "" + stats.didChange());
return stats;
}
/**
* Tighten method calls that occur within <code>node</code> and its children.
*/
public static void exec(JProgram program, JNode node) {
new MethodCallTightener(program).execImpl(node);
}
private final JProgram program;
private MethodCallTightener(JProgram program) {
this.program = program;
}
private OptimizerStats execImpl() {
MethodCallTighteningVisitor tightener = new MethodCallTighteningVisitor();
tightener.accept(program);
return new OptimizerStats(NAME).recordModified(tightener.getNumMods());
}
private void execImpl(JNode node) {
MethodCallTighteningVisitor tightener = new MethodCallTighteningVisitor();
tightener.accept(node);
}
}