KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > jjs > impl > MakeCallsStatic


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.dev.jjs.impl;
17
18 import com.google.gwt.dev.jjs.SourceInfo;
19 import com.google.gwt.dev.jjs.ast.Context;
20 import com.google.gwt.dev.jjs.ast.JClassType;
21 import com.google.gwt.dev.jjs.ast.JLocal;
22 import com.google.gwt.dev.jjs.ast.JLocalRef;
23 import com.google.gwt.dev.jjs.ast.JMethod;
24 import com.google.gwt.dev.jjs.ast.JMethodCall;
25 import com.google.gwt.dev.jjs.ast.JModVisitor;
26 import com.google.gwt.dev.jjs.ast.JParameter;
27 import com.google.gwt.dev.jjs.ast.JParameterRef;
28 import com.google.gwt.dev.jjs.ast.JProgram;
29 import com.google.gwt.dev.jjs.ast.JReturnStatement;
30 import com.google.gwt.dev.jjs.ast.JStatement;
31 import com.google.gwt.dev.jjs.ast.JThisRef;
32 import com.google.gwt.dev.jjs.ast.JType;
33 import com.google.gwt.dev.jjs.ast.JVisitor;
34
35 import java.util.HashSet JavaDoc;
36 import java.util.IdentityHashMap JavaDoc;
37 import java.util.Map JavaDoc;
38 import java.util.Set JavaDoc;
39
40 /**
41  * This is an interesting "optimization". It's not really an optimization in and
42  * of itself, but it opens the door to other optimizations. The basic idea is
43  * that you look for calls to instance methods that are not actually
44  * polymorphic. In other words, the target method is (effectively) final, not
45  * overriden anywhere in the compilation. We rewrite the single instance method
46  * as a static method that contains the implementation plus an instance method
47  * that delegates to the static method. Then we update any call sites to call
48  * the static method instead. This opens the door to further optimizations,
49  * reduces use of the long "this" keyword in the resulting JavaScript, and in
50  * most cases the polymorphic version can be pruned later.
51  *
52  * TODO(later): make this work on JSNI methods!
53  */

54 public class MakeCallsStatic {
55
56   /**
57    * For all methods that should be made static, move the contents of the method
58    * to a new static method, and have the original (instance) method delegate to
59    * it. Sometimes the instance method can be pruned later since we update all
60    * non-polymorphic call sites.
61    */

62   private class CreateStaticImplsVisitor extends JModVisitor {
63
64     // @Override
65
public boolean visit(JMethod x, Context ctx) {
66       if (!toBeMadeStatic.contains(x)) {
67         return false;
68       }
69
70       // Let's do it!
71
JClassType enclosingType = (JClassType) x.getEnclosingType();
72       JType oldReturnType = x.getType();
73
74       // Create the new static method
75
String JavaDoc newName = "$" + x.getName();
76
77       /*
78        * Don't use the JProgram helper because it auto-adds the new method to
79        * its enclosing class, which will break iteration.
80        */

81       JMethod newMethod = new JMethod(program, x.getSourceInfo(), newName,
82           enclosingType, oldReturnType, false, true, true, x.isPrivate());
83
84       // Setup all params and locals; map from the old method to the new method
85
JParameter thisParam = program.createParameter(null,
86           "this$static".toCharArray(), enclosingType, true, newMethod);
87       Map JavaDoc/* <JVariable, JVariable> */varMap = new IdentityHashMap JavaDoc();
88       for (int i = 0; i < x.params.size(); ++i) {
89         JParameter oldVar = (JParameter) x.params.get(i);
90         JParameter newVar = program.createParameter(oldVar.getSourceInfo(),
91             oldVar.getName().toCharArray(), oldVar.getType(), oldVar.isFinal(),
92             newMethod);
93         varMap.put(oldVar, newVar);
94       }
95       newMethod.freezeParamTypes();
96
97       // Copy all locals over to the new method
98
for (int i = 0; i < x.locals.size(); ++i) {
99         JLocal oldVar = (JLocal) x.locals.get(i);
100         JLocal newVar = program.createLocal(oldVar.getSourceInfo(),
101             oldVar.getName().toCharArray(), oldVar.getType(), oldVar.isFinal(),
102             newMethod);
103         varMap.put(oldVar, newVar);
104       }
105       x.locals.clear();
106
107       // Move the body of the instance method to the static method
108
newMethod.body.statements.addAll(x.body.statements);
109       x.body.statements.clear();
110
111       /*
112        * Rewrite the method body. Update all thisRefs to paramrefs. Update
113        * paramRefs and localRefs to target the params/locals in the new method.
114        */

115       RewriteMethodBody rewriter = new RewriteMethodBody(thisParam, varMap);
116       rewriter.accept(newMethod);
117
118       SourceInfo bodyInfo = x.body.getSourceInfo();
119       // delegate from the instance method to the static method
120
JMethodCall newCall = new JMethodCall(program, bodyInfo, null, newMethod);
121       newCall.getArgs().add(program.getExprThisRef(bodyInfo, enclosingType));
122       for (int i = 0; i < x.params.size(); ++i) {
123         JParameter param = (JParameter) x.params.get(i);
124         newCall.getArgs().add(new JParameterRef(program, bodyInfo, param));
125       }
126       JStatement statement;
127       if (oldReturnType == program.getTypeVoid()) {
128         statement = newCall.makeStatement();
129       } else {
130         statement = new JReturnStatement(program, bodyInfo, newCall);
131       }
132       x.body.statements.add(statement);
133
134       // Add the new method as a static impl of the old method
135
program.putStaticImpl(x, newMethod);
136       assert (ctx.canInsert());
137       ctx.insertAfter(newMethod);
138       return false;
139     }
140   }
141
142   /**
143    * Look for any places where instance methods are called in a static manner.
144    * Record this fact so we can create static dispatch implementations.
145    */

146   private class FindStaticDispatchSitesVisitor extends JVisitor {
147
148     // @Override
149
public void endVisit(JMethodCall x, Context ctx) {
150       JMethod method = x.getTarget();
151
152       // Did we already do this one?
153
if (program.getStaticImpl(method) != null
154           || toBeMadeStatic.contains(method)) {
155         return;
156       }
157
158       // Must be instance and final
159
if (x.canBePolymorphic()) {
160         return;
161       }
162       if (method.isStatic()) {
163         return;
164       }
165       if (method.isAbstract()) {
166         return;
167       }
168       if (method.isNative()) {
169         return;
170       }
171       if (method == program.getNullMethod()) {
172         // Special case: we don't make calls to this method static.
173
return;
174       }
175
176       // Let's do it!
177
toBeMadeStatic.add(method);
178     }
179   }
180
181   /**
182    * For any method calls to methods we updated during
183    * CreateStaticMethodVisitor, go and rewrite the call sites to call the static
184    * method instead.
185    */

186   private class RewriteCallSites extends JModVisitor {
187
188     /*
189      * In cases where callers are directly referencing (effectively) final
190      * instance methods, rewrite the call site to reference the newly-generated
191      * static method instead.
192      */

193     // @Override
194
public void endVisit(JMethodCall x, Context ctx) {
195       JMethod oldMethod = x.getTarget();
196       JMethod newMethod = program.getStaticImpl(oldMethod);
197       if (newMethod == null || x.canBePolymorphic()) {
198         return;
199       }
200
201       // Update the call site
202
JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(), null,
203           newMethod);
204
205       // The qualifier becomes the first arg
206
newCall.getArgs().add(x.getInstance());
207       // Copy the rest of the args
208
for (int i = 0; i < x.getArgs().size(); ++i) {
209         newCall.getArgs().add(x.getArgs().get(i));
210       }
211       ctx.replaceMe(newCall);
212     }
213   }
214
215   /**
216    * When code is moved from an instance method to a static method, all this
217    * refs must be replaced with param refs to the synthetic this param.
218    */

219   private class RewriteMethodBody extends JModVisitor {
220
221     private final JParameter thisParam;
222     private final Map JavaDoc/* <JVariable, JVariable> */varMap;
223
224     public RewriteMethodBody(JParameter thisParam,
225         Map JavaDoc/* <JVariable, JVariable> */varMap) {
226       this.thisParam = thisParam;
227       this.varMap = varMap;
228     }
229
230     // @Override
231
public void endVisit(JLocalRef x, Context ctx) {
232       JLocal local = (JLocal) varMap.get(x.getTarget());
233       JLocalRef localRef = new JLocalRef(program, x.getSourceInfo(), local);
234       ctx.replaceMe(localRef);
235     }
236
237     // @Override
238
public void endVisit(JParameterRef x, Context ctx) {
239       JParameter param = (JParameter) varMap.get(x.getTarget());
240       JParameterRef paramRef = new JParameterRef(program, x.getSourceInfo(),
241           param);
242       ctx.replaceMe(paramRef);
243     }
244
245     // @Override
246
public void endVisit(JThisRef x, Context ctx) {
247       JParameterRef paramRef = new JParameterRef(program, x.getSourceInfo(),
248           thisParam);
249       ctx.replaceMe(paramRef);
250     }
251   }
252
253   public static boolean exec(JProgram program) {
254     return new MakeCallsStatic(program).execImpl();
255   }
256
257   public Set JavaDoc toBeMadeStatic = new HashSet JavaDoc();
258
259   private final JProgram program;
260
261   private MakeCallsStatic(JProgram program) {
262     this.program = program;
263   }
264
265   private boolean execImpl() {
266     FindStaticDispatchSitesVisitor finder = new FindStaticDispatchSitesVisitor();
267     finder.accept(program);
268     if (toBeMadeStatic.isEmpty()) {
269       return false;
270     }
271
272     CreateStaticImplsVisitor creator = new CreateStaticImplsVisitor();
273     creator.accept(program);
274     if (!creator.didChange()) {
275       return false;
276     }
277
278     RewriteCallSites rewriter = new RewriteCallSites();
279     rewriter.accept(program);
280     assert (rewriter.didChange());
281     return true;
282   }
283
284 }
285
Popular Tags