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.jjs.impl;
017:
018: import com.google.gwt.dev.jjs.ast.Context;
019: import com.google.gwt.dev.jjs.ast.JArrayType;
020: import com.google.gwt.dev.jjs.ast.JClassType;
021: import com.google.gwt.dev.jjs.ast.JExpression;
022: import com.google.gwt.dev.jjs.ast.JInterfaceType;
023: import com.google.gwt.dev.jjs.ast.JMethod;
024: import com.google.gwt.dev.jjs.ast.JMethodCall;
025: import com.google.gwt.dev.jjs.ast.JModVisitor;
026: import com.google.gwt.dev.jjs.ast.JNullType;
027: import com.google.gwt.dev.jjs.ast.JProgram;
028: import com.google.gwt.dev.jjs.ast.JReferenceType;
029: import com.google.gwt.dev.jjs.ast.JType;
030:
031: /**
032: * Update polymorphic method calls to tighter bindings based on the type of the
033: * qualifier. For a given polymorphic method call to a non-final target, see if
034: * the static type of the qualifer would let us target an override instead.
035: *
036: * This is possible because the qualifier might have been tightened by
037: * {@link com.google.gwt.dev.jjs.impl.TypeTightener}.
038: */
039: public class MethodCallTightener {
040:
041: /**
042: * Updates polymorphic method calls to tighter bindings based on the type of
043: * the qualifier.
044: */
045: public class MethodCallTighteningVisitor extends JModVisitor {
046:
047: // @Override
048: public void endVisit(JMethodCall x, Context ctx) {
049: JMethod method = x.getTarget();
050: JExpression instance = x.getInstance();
051:
052: // The method call is already known statically
053: if (!x.canBePolymorphic()) {
054: return;
055: }
056:
057: JType instanceType = instance.getType();
058: JReferenceType enclosingType = method.getEnclosingType();
059:
060: if (instanceType == enclosingType
061: || instanceType instanceof JInterfaceType) {
062: // This method call is as tight as it can be for the type of the
063: // qualifier
064: return;
065: }
066:
067: if (instanceType instanceof JArrayType) {
068: // shouldn't get here; arrays don't have extra methods
069: return;
070: }
071:
072: if (instanceType instanceof JNullType) {
073: // TypeTightener will handle this case
074: return;
075: }
076:
077: assert (instanceType instanceof JClassType);
078:
079: /*
080: * Search myself and all my super types to find a tighter implementation
081: * of the called method, if possible.
082: */
083: JMethod foundMethod = null;
084: JClassType type;
085: outer: for (type = (JClassType) instanceType; type != null
086: && type != enclosingType; type = type.extnds) {
087: for (int i = 0; i < type.methods.size(); ++i) {
088: JMethod methodIt = (JMethod) type.methods.get(i);
089: if (JProgram.methodsDoMatch(method, methodIt)) {
090: foundMethod = methodIt;
091: break outer;
092: }
093: }
094: }
095:
096: if (foundMethod == null) {
097: return;
098: }
099:
100: /*
101: * Replace the call to the original method with a call to the same method
102: * on the tighter type.
103: */
104: JMethodCall call = new JMethodCall(program, x
105: .getSourceInfo(), x.getInstance(), foundMethod);
106: call.getArgs().addAll(x.getArgs());
107: ctx.replaceMe(call);
108: }
109: }
110:
111: public static boolean exec(JProgram program) {
112: return new MethodCallTightener(program).execImpl();
113: }
114:
115: private final JProgram program;
116:
117: private MethodCallTightener(JProgram program) {
118: this .program = program;
119: }
120:
121: private boolean execImpl() {
122: MethodCallTighteningVisitor tightener = new MethodCallTighteningVisitor();
123: tightener.accept(program);
124: return tightener.didChange();
125: }
126:
127: }
|