Source Code Cross Referenced for Pruner.java in  » Ajax » GWT » com » google » gwt » dev » jjs » impl » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Ajax » GWT » com.google.gwt.dev.jjs.impl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.CanBeStatic;
019:        import com.google.gwt.dev.jjs.ast.Context;
020:        import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
021:        import com.google.gwt.dev.jjs.ast.JArrayType;
022:        import com.google.gwt.dev.jjs.ast.JBinaryOperation;
023:        import com.google.gwt.dev.jjs.ast.JBinaryOperator;
024:        import com.google.gwt.dev.jjs.ast.JBlock;
025:        import com.google.gwt.dev.jjs.ast.JClassType;
026:        import com.google.gwt.dev.jjs.ast.JExpression;
027:        import com.google.gwt.dev.jjs.ast.JField;
028:        import com.google.gwt.dev.jjs.ast.JFieldRef;
029:        import com.google.gwt.dev.jjs.ast.JInterfaceType;
030:        import com.google.gwt.dev.jjs.ast.JLocal;
031:        import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
032:        import com.google.gwt.dev.jjs.ast.JLocalRef;
033:        import com.google.gwt.dev.jjs.ast.JMethod;
034:        import com.google.gwt.dev.jjs.ast.JMethodBody;
035:        import com.google.gwt.dev.jjs.ast.JMethodCall;
036:        import com.google.gwt.dev.jjs.ast.JModVisitor;
037:        import com.google.gwt.dev.jjs.ast.JNewArray;
038:        import com.google.gwt.dev.jjs.ast.JNewInstance;
039:        import com.google.gwt.dev.jjs.ast.JNode;
040:        import com.google.gwt.dev.jjs.ast.JParameter;
041:        import com.google.gwt.dev.jjs.ast.JParameterRef;
042:        import com.google.gwt.dev.jjs.ast.JPrimitiveType;
043:        import com.google.gwt.dev.jjs.ast.JProgram;
044:        import com.google.gwt.dev.jjs.ast.JReferenceType;
045:        import com.google.gwt.dev.jjs.ast.JStringLiteral;
046:        import com.google.gwt.dev.jjs.ast.JThisRef;
047:        import com.google.gwt.dev.jjs.ast.JType;
048:        import com.google.gwt.dev.jjs.ast.JVariable;
049:        import com.google.gwt.dev.jjs.ast.JVariableRef;
050:        import com.google.gwt.dev.jjs.ast.JVisitor;
051:        import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
052:        import com.google.gwt.dev.jjs.ast.js.JsniFieldRef;
053:        import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
054:        import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
055:        import com.google.gwt.dev.js.ast.JsContext;
056:        import com.google.gwt.dev.js.ast.JsExpression;
057:        import com.google.gwt.dev.js.ast.JsFunction;
058:        import com.google.gwt.dev.js.ast.JsName;
059:        import com.google.gwt.dev.js.ast.JsNameRef;
060:        import com.google.gwt.dev.js.ast.JsVisitor;
061:
062:        import java.util.ArrayList;
063:        import java.util.HashMap;
064:        import java.util.HashSet;
065:        import java.util.Iterator;
066:        import java.util.List;
067:        import java.util.Map;
068:        import java.util.Set;
069:
070:        /**
071:         * Remove globally unreferenced classes, interfaces, methods, parameters, and
072:         * fields from the AST. This algorithm is based on having known "entry points"
073:         * into the application which serve as the root(s) from which reachability is
074:         * determined and everything else is rescued. Pruner determines reachability at
075:         * a global level based on method calls and new operations; it does not perform
076:         * any local code flow analysis. But, a local code flow optimization pass that
077:         * can eliminate method calls would allow Pruner to prune additional nodes.
078:         * 
079:         * Note: references to pruned types may still exist in the tree after this pass
080:         * runs, however, it should only be in contexts that do not rely on any code
081:         * generation for the pruned type. For example, it's legal to have a variable of
082:         * a pruned type, or to try to cast to a pruned type. These will cause natural
083:         * failures at run time; or later optimizations might be able to hard-code
084:         * failures at compile time.
085:         * 
086:         * Note: this class is limited to pruning parameters of static methods only.
087:         */
088:        public class Pruner {
089:
090:            /**
091:             * Remove assignments to pruned fields, locals and params. Also nullify the
092:             * return type of methods declared to return a globally uninstantiable type.
093:             */
094:            private class CleanupRefsVisitor extends JModVisitor {
095:
096:                @Override
097:                public void endVisit(JBinaryOperation x, Context ctx) {
098:                    // The LHS of assignments may have been pruned.
099:                    if (x.getOp() == JBinaryOperator.ASG) {
100:                        JExpression lhs = x.getLhs();
101:                        if (lhs.hasSideEffects()) {
102:                            return;
103:                        }
104:                        if (lhs instanceof  JFieldRef
105:                                || lhs instanceof  JLocalRef
106:                                || lhs instanceof  JParameterRef) {
107:                            JVariable var = ((JVariableRef) lhs).getTarget();
108:                            if (!referencedNonTypes.contains(var)) {
109:                                // Just replace with my RHS
110:                                ctx.replaceMe(x.getRhs());
111:                            }
112:                        }
113:                    }
114:                }
115:
116:                @Override
117:                public void endVisit(JMethod x, Context ctx) {
118:                    JType type = x.getType();
119:                    if (type instanceof  JReferenceType) {
120:                        if (!program.typeOracle
121:                                .isInstantiatedType((JReferenceType) type)) {
122:                            x.setType(program.getTypeNull());
123:                        }
124:                    }
125:                }
126:
127:                public void endVisit(JLocalDeclarationStatement x, Context ctx) {
128:                    // The variable may have been pruned.
129:                    if (!referencedNonTypes.contains(x.getLocalRef()
130:                            .getTarget())) {
131:                        // If there is an initializer, just replace with that.
132:                        if (x.getInitializer() != null) {
133:                            ctx.replaceMe(x.getInitializer().makeStatement());
134:                        } else {
135:                            // No initializer, prune this entirely.
136:                            if (ctx.canRemove()) {
137:                                // Just remove it if we can.
138:                                ctx.removeMe();
139:                            } else {
140:                                // Replace with empty block.
141:                                ctx.replaceMe(new JBlock(program, x
142:                                        .getSourceInfo()));
143:                            }
144:                        }
145:                    }
146:                }
147:
148:                @Override
149:                public void endVisit(JMethodCall x, Context ctx) {
150:                    JMethod method = x.getTarget();
151:
152:                    // Did we prune the parameters of the method we're calling?
153:                    if (methodToOriginalParamsMap.containsKey(method)) {
154:                        // This must be a static method
155:                        assert method.isStatic();
156:
157:                        JMethodCall newCall = new JMethodCall(program, x
158:                                .getSourceInfo(), x.getInstance(), method);
159:
160:                        ArrayList<JExpression> args = x.getArgs();
161:                        ArrayList<JParameter> originalParams = methodToOriginalParamsMap
162:                                .get(method);
163:
164:                        JMultiExpression currentMulti = null;
165:                        for (int i = 0, c = args.size(); i < c; ++i) {
166:                            JExpression arg = args.get(i);
167:                            JParameter param = null;
168:                            if (i < originalParams.size()) {
169:                                param = originalParams.get(i);
170:                            }
171:
172:                            if (param != null
173:                                    && referencedNonTypes.contains(param)) {
174:                                // If there is an existing multi, terminate it.
175:                                if (currentMulti != null) {
176:                                    currentMulti.exprs.add(arg);
177:                                    newCall.getArgs().add(currentMulti);
178:                                    currentMulti = null;
179:                                } else {
180:                                    newCall.getArgs().add(arg);
181:                                }
182:                            } else if (arg.hasSideEffects()) {
183:                                // The argument is only needed for side effects, add it to a multi.
184:                                if (currentMulti == null) {
185:                                    currentMulti = new JMultiExpression(
186:                                            program, x.getSourceInfo());
187:                                }
188:                                currentMulti.exprs.add(arg);
189:                            }
190:                        }
191:
192:                        // Add any orphaned parameters on the end. Extra params are OK.
193:                        if (currentMulti != null) {
194:                            newCall.getArgs().add(currentMulti);
195:                        }
196:
197:                        ctx.replaceMe(newCall);
198:                    }
199:                }
200:            }
201:
202:            /**
203:             * Remove any unreferenced classes and interfaces from JProgram. Remove any
204:             * unreferenced methods and fields from their containing classes.
205:             */
206:            private class PruneVisitor extends JVisitor {
207:
208:                private boolean didChange = false;
209:
210:                @Override
211:                public boolean didChange() {
212:                    return didChange;
213:                }
214:
215:                @Override
216:                public boolean visit(JClassType type, Context ctx) {
217:
218:                    assert (referencedTypes.contains(type));
219:                    boolean isInstantiated = program.typeOracle
220:                            .isInstantiatedType(type);
221:
222:                    for (Iterator<JField> it = type.fields.iterator(); it
223:                            .hasNext();) {
224:                        JField field = it.next();
225:                        if (!referencedNonTypes.contains(field)
226:                                || pruneViaNoninstantiability(isInstantiated,
227:                                        field)) {
228:                            it.remove();
229:                            didChange = true;
230:                        }
231:                    }
232:
233:                    for (Iterator<JMethod> it = type.methods.iterator(); it
234:                            .hasNext();) {
235:                        JMethod method = it.next();
236:                        if (!methodIsReferenced(method)
237:                                || pruneViaNoninstantiability(isInstantiated,
238:                                        method)) {
239:                            it.remove();
240:                            didChange = true;
241:                        } else {
242:                            accept(method);
243:                        }
244:                    }
245:
246:                    return false;
247:                }
248:
249:                @Override
250:                public boolean visit(JInterfaceType type, Context ctx) {
251:                    boolean isReferenced = referencedTypes.contains(type);
252:                    boolean isInstantiated = program.typeOracle
253:                            .isInstantiatedType(type);
254:
255:                    for (Iterator<JField> it = type.fields.iterator(); it
256:                            .hasNext();) {
257:                        JField field = it.next();
258:                        // all interface fields are static and final
259:                        if (!isReferenced
260:                                || !referencedNonTypes.contains(field)) {
261:                            it.remove();
262:                            didChange = true;
263:                        }
264:                    }
265:
266:                    Iterator<JMethod> it = type.methods.iterator();
267:                    if (it.hasNext()) {
268:                        // start at index 1; never prune clinit directly out of the interface
269:                        it.next();
270:                    }
271:                    while (it.hasNext()) {
272:                        JMethod method = it.next();
273:                        // all other interface methods are instance and abstract
274:                        if (!isInstantiated || !methodIsReferenced(method)) {
275:                            it.remove();
276:                            didChange = true;
277:                        }
278:                    }
279:
280:                    return false;
281:                }
282:
283:                @Override
284:                public boolean visit(JMethod x, Context ctx) {
285:                    if (x.isStatic()) {
286:                        /*
287:                         * Don't prune parameters on unreferenced methods. The methods might not
288:                         * be reachable through the current method traversal routines, but might
289:                         * be used or checked elsewhere.
290:                         * 
291:                         * Basically, if we never actually checked if the method parameters were
292:                         * used or not, don't prune them. Doing so would leave a number of
293:                         * dangling JParameterRefs that blow up in later optimizations.
294:                         */
295:                        if (!referencedNonTypes.contains(x)) {
296:                            return true;
297:                        }
298:
299:                        /*
300:                         * We cannot prune parameters from staticImpls that still have a live
301:                         * instance method, because doing so would screw up any subsequent
302:                         * devirtualizations. If the instance method has been pruned, then it's
303:                         * okay. Also, it's okay on the final pass since no more
304:                         * devirtualizations will occur.
305:                         */
306:                        JMethod staticImplFor = program.staticImplFor(x);
307:                        // Unless the instance method has already been pruned, of course.
308:                        if (saveCodeGenTypes
309:                                && staticImplFor != null
310:                                && staticImplFor.getEnclosingType().methods
311:                                        .contains(staticImplFor)) {
312:                            // instance method is still live
313:                            return true;
314:                        }
315:
316:                        JsFunction func = x.isNative() ? ((JsniMethodBody) x
317:                                .getBody()).getFunc() : null;
318:
319:                        ArrayList<JParameter> originalParams = new ArrayList<JParameter>(
320:                                x.params);
321:
322:                        for (int i = 0; i < x.params.size(); ++i) {
323:                            JParameter param = x.params.get(i);
324:                            if (!referencedNonTypes.contains(param)) {
325:                                x.params.remove(i);
326:                                didChange = true;
327:                                // Remove the associated JSNI parameter
328:                                if (func != null) {
329:                                    func.getParameters().remove(i);
330:                                }
331:                                --i;
332:                                methodToOriginalParamsMap
333:                                        .put(x, originalParams);
334:                            }
335:                        }
336:                    }
337:
338:                    return true;
339:                }
340:
341:                @Override
342:                public boolean visit(JMethodBody x, Context ctx) {
343:                    for (Iterator<JLocal> it = x.locals.iterator(); it
344:                            .hasNext();) {
345:                        JLocal local = it.next();
346:                        if (!referencedNonTypes.contains(local)) {
347:                            it.remove();
348:                            didChange = true;
349:                        }
350:                    }
351:                    return false;
352:                }
353:
354:                @Override
355:                public boolean visit(JProgram program, Context ctx) {
356:                    for (Iterator<JReferenceType> it = program
357:                            .getDeclaredTypes().iterator(); it.hasNext();) {
358:                        JReferenceType type = it.next();
359:                        if (referencedTypes.contains(type)
360:                                || program.typeOracle.isInstantiatedType(type)) {
361:                            accept(type);
362:                        } else {
363:                            it.remove();
364:                            didChange = true;
365:                        }
366:                    }
367:                    return false;
368:                }
369:
370:                /**
371:                 * Returns <code>true</code> if a method is referenced.
372:                 */
373:                private boolean methodIsReferenced(JMethod method) {
374:                    // Is the method directly referenced?
375:                    if (referencedNonTypes.contains(method)) {
376:                        return true;
377:                    }
378:
379:                    /*
380:                     * Special case: if method is the static impl for a live instance method,
381:                     * don't prune it unless this is the final prune.
382:                     * 
383:                     * In some cases, the staticImpl can be inlined into the instance method
384:                     * but still be needed at other call sites.
385:                     */
386:                    JMethod staticImplFor = program.staticImplFor(method);
387:                    if (staticImplFor != null
388:                            && referencedNonTypes.contains(staticImplFor)) {
389:                        if (saveCodeGenTypes) {
390:                            return true;
391:                        }
392:                    }
393:                    return false;
394:                }
395:
396:                private boolean pruneViaNoninstantiability(
397:                        boolean isInstantiated, CanBeStatic it) {
398:                    return (!isInstantiated && !it.isStatic());
399:                }
400:            }
401:
402:            /**
403:             * Marks as "referenced" any types, methods, and fields that are reachable.
404:             * Also marks as "instantiable" any the classes and interfaces that can
405:             * possibly be instantiated.
406:             * 
407:             * TODO(later): make RescueVisitor use less stack?
408:             */
409:            private class RescueVisitor extends JVisitor {
410:
411:                private final Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
412:
413:                public void commitInstantiatedTypes() {
414:                    program.typeOracle.setInstantiatedTypes(instantiatedTypes);
415:                }
416:
417:                @Override
418:                public boolean visit(JArrayType type, Context ctx) {
419:                    assert (referencedTypes.contains(type));
420:                    boolean isInstantiated = instantiatedTypes.contains(type);
421:
422:                    JType leafType = type.getLeafType();
423:                    int dims = type.getDims();
424:
425:                    // Rescue my super array type
426:                    if (leafType instanceof  JReferenceType) {
427:                        JReferenceType rLeafType = (JReferenceType) leafType;
428:                        if (rLeafType.extnds != null) {
429:                            JArrayType super Array = program.getTypeArray(
430:                                    rLeafType.extnds, dims);
431:                            rescue(super Array, true, isInstantiated);
432:                        }
433:
434:                        for (int i = 0; i < rLeafType.implments.size(); ++i) {
435:                            JInterfaceType intfType = rLeafType.implments
436:                                    .get(i);
437:                            JArrayType intfArray = program.getTypeArray(
438:                                    intfType, dims);
439:                            rescue(intfArray, true, isInstantiated);
440:                        }
441:                    }
442:
443:                    return false;
444:                }
445:
446:                @Override
447:                public boolean visit(JBinaryOperation x, Context ctx) {
448:                    // special string concat handling
449:                    if (x.getOp() == JBinaryOperator.ADD
450:                            && x.getType() == program.getTypeJavaLangString()) {
451:                        rescueByConcat(x.getLhs().getType());
452:                        rescueByConcat(x.getRhs().getType());
453:                    } else if (x.getOp() == JBinaryOperator.ASG) {
454:                        // Don't rescue variables that are merely assigned to and never read
455:                        boolean doSkip = false;
456:                        JExpression lhs = x.getLhs();
457:                        if (lhs.hasSideEffects()) {
458:                            // cannot skip
459:                        } else if (lhs instanceof  JLocalRef) {
460:                            // locals are ok to skip
461:                            doSkip = true;
462:                        } else if (lhs instanceof  JParameterRef) {
463:                            // parameters are ok to skip
464:                            doSkip = true;
465:                        } else if (lhs instanceof  JFieldRef) {
466:                            JFieldRef fieldRef = (JFieldRef) lhs;
467:                            /*
468:                             * Whether we can skip depends on what the qualifier is; we have to be
469:                             * certain the qualifier cannot be a null pointer (in which case we'd
470:                             * fail to throw the appropriate exception.
471:                             * 
472:                             * TODO: better non-null tracking!
473:                             */
474:                            JExpression instance = fieldRef.getInstance();
475:                            if (fieldRef.getField().isStatic()) {
476:                                // statics are always okay
477:                                doSkip = true;
478:                            } else if (instance instanceof  JThisRef) {
479:                                // a this ref cannot be null
480:                                doSkip = true;
481:                            }
482:                        }
483:
484:                        if (doSkip) {
485:                            accept(x.getRhs());
486:                            return false;
487:                        }
488:                    }
489:                    return true;
490:                }
491:
492:                @Override
493:                public boolean visit(JClassType type, Context ctx) {
494:                    assert (referencedTypes.contains(type));
495:                    boolean isInstantiated = instantiatedTypes.contains(type);
496:
497:                    /*
498:                     * SPECIAL: Some classes contain methods used by code generation later.
499:                     * Unless those transforms have already been performed, we must rescue all
500:                     * contained methods for later user.
501:                     */
502:                    if (saveCodeGenTypes && program.codeGenTypes.contains(type)) {
503:                        for (int i = 0; i < type.methods.size(); ++i) {
504:                            JMethod it = type.methods.get(i);
505:                            rescue(it);
506:                        }
507:                    }
508:
509:                    // Rescue my super type
510:                    rescue(type.extnds, true, isInstantiated);
511:
512:                    // Rescue my clinit (it won't ever be explicitly referenced
513:                    rescue(type.methods.get(0));
514:
515:                    // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
516:                    // However, if I'm instantiated, let's mark them as instantiated.
517:                    for (int i = 0; i < type.implments.size(); ++i) {
518:                        JInterfaceType intfType = type.implments.get(i);
519:                        rescue(intfType, false, isInstantiated);
520:                    }
521:
522:                    return false;
523:                }
524:
525:                @Override
526:                public boolean visit(JFieldRef ref, Context ctx) {
527:                    JField target = ref.getField();
528:
529:                    // JLS 12.4.1: references to static, non-final, or
530:                    // non-compile-time-constant fields rescue the enclosing class.
531:                    // JDT already folds in compile-time constants as literals, so we must
532:                    // rescue the enclosing types for any static fields that make it here.
533:                    if (target.isStatic()) {
534:                        rescue(target.getEnclosingType(), true, false);
535:                    }
536:                    rescue(target);
537:                    return true;
538:                }
539:
540:                @Override
541:                public boolean visit(JInterfaceType type, Context ctx) {
542:                    boolean isReferenced = referencedTypes.contains(type);
543:                    boolean isInstantiated = instantiatedTypes.contains(type);
544:                    assert (isReferenced || isInstantiated);
545:
546:                    // Rescue my clinit (it won't ever be explicitly referenced
547:                    rescue(type.methods.get(0));
548:
549:                    // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
550:                    // However, if I'm instantiated, let's mark them as instantiated.
551:                    if (isInstantiated) {
552:                        for (int i = 0; i < type.implments.size(); ++i) {
553:                            JInterfaceType intfType = type.implments.get(i);
554:                            rescue(intfType, false, true);
555:                        }
556:                    }
557:
558:                    // visit any field initializers
559:                    for (int i = 0; i < type.fields.size(); ++i) {
560:                        JField it = type.fields.get(i);
561:                        accept(it);
562:                    }
563:
564:                    return false;
565:                }
566:
567:                public boolean visit(JLocalDeclarationStatement x, Context ctx) {
568:                    /*
569:                     * A declaration by itself doesn't rescue a local (even if it has an
570:                     * initializer). Writes don't count, only reads.
571:                     */
572:                    if (x.getInitializer() != null) {
573:                        accept(x.getInitializer());
574:                    }
575:                    return false;
576:                }
577:
578:                @Override
579:                public boolean visit(JLocalRef ref, Context ctx) {
580:                    JLocal target = ref.getLocal();
581:                    rescue(target);
582:                    return true;
583:                }
584:
585:                @Override
586:                public boolean visit(final JMethod x, Context ctx) {
587:                    if (x.isNative()) {
588:                        // Manually rescue native parameter references
589:                        final JsniMethodBody body = (JsniMethodBody) x
590:                                .getBody();
591:                        final JsFunction func = body.getFunc();
592:
593:                        new JsVisitor() {
594:                            @Override
595:                            public void endVisit(JsNameRef nameRef,
596:                                    JsContext<JsExpression> ctx) {
597:                                JsName ident = nameRef.getName();
598:
599:                                if (ident != null) {
600:                                    // If we're referencing a parameter, rescue the associated
601:                                    // JParameter
602:                                    int index = func.getParameters().indexOf(
603:                                            ident.getStaticRef());
604:                                    if (index != -1) {
605:                                        rescue(x.params.get(index));
606:                                    }
607:                                }
608:                            }
609:                        }.accept(func);
610:                    }
611:
612:                    return true;
613:                }
614:
615:                @Override
616:                public boolean visit(JMethodCall call, Context ctx) {
617:                    JMethod target = call.getTarget();
618:                    // JLS 12.4.1: references to static methods rescue the enclosing class
619:                    if (target.isStatic()) {
620:                        rescue(target.getEnclosingType(), true, false);
621:                    }
622:                    rescue(target);
623:                    return true;
624:                }
625:
626:                @Override
627:                public boolean visit(JNewArray newArray, Context ctx) {
628:                    // rescue and instantiate the array type
629:                    JArrayType arrayType = newArray.getArrayType();
630:                    if (newArray.dims != null) {
631:                        // rescue my type and all the implicitly nested types (with fewer dims)
632:                        int nDims = arrayType.getDims();
633:                        JType leafType = arrayType.getLeafType();
634:                        assert (newArray.dims.size() == nDims);
635:                        for (int i = 0; i < nDims; ++i) {
636:                            if (newArray.dims.get(i) instanceof  JAbsentArrayDimension) {
637:                                break;
638:                            }
639:                            rescue(program.getTypeArray(leafType, nDims - i),
640:                                    true, true);
641:                        }
642:                    } else {
643:                        // just rescue my own specific type
644:                        rescue(arrayType, true, true);
645:                    }
646:                    return true;
647:                }
648:
649:                @Override
650:                public boolean visit(JNewInstance newInstance, Context ctx) {
651:                    // rescue and instantiate the target class!
652:                    rescue(newInstance.getClassType(), true, true);
653:                    return true;
654:                }
655:
656:                @Override
657:                public boolean visit(JParameterRef x, Context ctx) {
658:                    // rescue the parameter for future pruning purposes
659:                    rescue(x.getParameter());
660:                    return true;
661:                }
662:
663:                @Override
664:                public boolean visit(JsniFieldRef x, Context ctx) {
665:                    /*
666:                     * SPECIAL: this could be an assignment that passes a value from
667:                     * JavaScript into Java.
668:                     * 
669:                     * TODO(later): technically we only need to do this if the field is being
670:                     * assigned to.
671:                     */
672:                    maybeRescueJavaScriptObjectPassingIntoJava(x.getField()
673:                            .getType());
674:                    // JsniFieldRef rescues as JFieldRef
675:                    return visit((JFieldRef) x, ctx);
676:                }
677:
678:                @Override
679:                public boolean visit(JsniMethodRef x, Context ctx) {
680:                    /*
681:                     * SPECIAL: each argument of the call passes a value from JavaScript into
682:                     * Java.
683:                     */
684:                    ArrayList<JParameter> params = x.getTarget().params;
685:                    for (int i = 0, c = params.size(); i < c; ++i) {
686:                        JParameter param = params.get(i);
687:                        maybeRescueJavaScriptObjectPassingIntoJava(param
688:                                .getType());
689:
690:                        /*
691:                         * Because we're not currently tracking methods through JSNI, we need to
692:                         * assume that it's not safe to prune parameters of a method referenced
693:                         * as such.
694:                         * 
695:                         * A better solution would be to perform basic escape analysis to ensure
696:                         * that the function reference never escapes, or at minimum, ensure that
697:                         * the method is immediately called after retrieving the method
698:                         * reference.
699:                         */
700:                        rescue(param);
701:                    }
702:                    // JsniMethodRef rescues as JMethodCall
703:                    return visit((JMethodCall) x, ctx);
704:                }
705:
706:                @Override
707:                public boolean visit(JStringLiteral literal, Context ctx) {
708:                    // rescue and instantiate java.lang.String
709:                    rescue(program.getTypeJavaLangString(), true, true);
710:                    return true;
711:                }
712:
713:                /**
714:                 * Subclasses of JavaScriptObject are never instantiated directly. They are
715:                 * created "magically" when a JSNI method passes a reference to an existing
716:                 * JS object into Java code. The point at which a subclass of JSO is passed
717:                 * into Java code constitutes "instantiation". We must identify these points
718:                 * and trigger a rescue and instantiation of that particular JSO subclass.
719:                 * 
720:                 * @param type The type of the value passing from Java to JavaScript.
721:                 * @see com.google.gwt.core.client.JavaScriptObject
722:                 */
723:                private void maybeRescueJavaScriptObjectPassingIntoJava(
724:                        JType type) {
725:                    if (type instanceof  JReferenceType) {
726:                        JReferenceType refType = (JReferenceType) type;
727:                        if (program.typeOracle.canTriviallyCast(refType,
728:                                program.getJavaScriptObject())) {
729:                            rescue(refType, true, true);
730:                        }
731:                    }
732:                }
733:
734:                private boolean rescue(JMethod method) {
735:                    if (method != null) {
736:                        if (!referencedNonTypes.contains(method)) {
737:                            referencedNonTypes.add(method);
738:                            accept(method);
739:                            if (method.isNative()) {
740:                                /*
741:                                 * SPECIAL: returning from this method passes a value from
742:                                 * JavaScript into Java.
743:                                 */
744:                                maybeRescueJavaScriptObjectPassingIntoJava(method
745:                                        .getType());
746:                            }
747:                            return true;
748:                        }
749:                    }
750:                    return false;
751:                }
752:
753:                private void rescue(JReferenceType type, boolean isReferenced,
754:                        boolean isInstantiated) {
755:                    if (type != null) {
756:
757:                        boolean doVisit = false;
758:                        if (isInstantiated && !instantiatedTypes.contains(type)) {
759:                            instantiatedTypes.add(type);
760:                            doVisit = true;
761:                        }
762:
763:                        if (isReferenced && !referencedTypes.contains(type)) {
764:                            referencedTypes.add(type);
765:                            doVisit = true;
766:                        }
767:
768:                        if (doVisit) {
769:                            accept(type);
770:                        }
771:                    }
772:                }
773:
774:                private void rescue(JVariable var) {
775:                    if (var != null) {
776:                        if (!referencedNonTypes.contains(var)) {
777:                            referencedNonTypes.add(var);
778:                        }
779:                    }
780:                }
781:
782:                /**
783:                 * Handle special rescues needed implicitly to support concat.
784:                 */
785:                private void rescueByConcat(JType type) {
786:                    JClassType stringType = program.getTypeJavaLangString();
787:                    JPrimitiveType charType = program.getTypePrimitiveChar();
788:                    if (type instanceof  JReferenceType && type != stringType) {
789:                        /*
790:                         * Any reference types (except String, which works by default) that take
791:                         * part in a concat must rescue java.lang.Object.toString().
792:                         * 
793:                         * TODO: can we narrow the focus by walking up the type heirarchy or
794:                         * doing explicit toString calls?
795:                         */
796:                        JMethod toStringMethod = program
797:                                .getIndexedMethod("Object.toString");
798:                        rescue(toStringMethod);
799:                    } else if (type == charType) {
800:                        /*
801:                         * Characters must rescue String.valueOf(char)
802:                         */
803:                        if (stringValueOfChar == null) {
804:                            for (int i = 0; i < stringType.methods.size(); ++i) {
805:                                JMethod meth = stringType.methods.get(i);
806:                                if (meth.getName().equals("valueOf")) {
807:                                    List<JType> params = meth
808:                                            .getOriginalParamTypes();
809:                                    if (params.size() == 1) {
810:                                        if (params.get(0) == charType) {
811:                                            stringValueOfChar = meth;
812:                                            break;
813:                                        }
814:                                    }
815:                                }
816:                            }
817:                            assert (stringValueOfChar != null);
818:                        }
819:                        rescue(stringValueOfChar);
820:                    }
821:                }
822:            }
823:
824:            /**
825:             * A method that isn't called directly can still be needed, if it overrides or
826:             * implements any methods that are called.
827:             */
828:            private class UpRefVisitor extends JVisitor {
829:
830:                private boolean didRescue = false;
831:                private final RescueVisitor rescuer;
832:
833:                public UpRefVisitor(RescueVisitor rescuer) {
834:                    this .rescuer = rescuer;
835:                }
836:
837:                public boolean didRescue() {
838:                    return didRescue;
839:                }
840:
841:                @Override
842:                public boolean visit(JMethod x, Context ctx) {
843:                    if (referencedNonTypes.contains(x)) {
844:                        return false;
845:                    }
846:
847:                    for (JMethod override : program.typeOracle
848:                            .getAllOverrides(x)) {
849:                        if (referencedNonTypes.contains(override)) {
850:                            rescuer.rescue(x);
851:                            didRescue = true;
852:                            return false;
853:                        }
854:                    }
855:                    return false;
856:                }
857:
858:                @Override
859:                public boolean visit(JProgram x, Context ctx) {
860:                    didRescue = false;
861:                    return true;
862:                }
863:            }
864:
865:            public static boolean exec(JProgram program, boolean noSpecialTypes) {
866:                return new Pruner(program, noSpecialTypes).execImpl();
867:            }
868:
869:            private final JProgram program;
870:            private final Set<JNode> referencedNonTypes = new HashSet<JNode>();
871:            private final Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
872:            private final Map<JMethod, ArrayList<JParameter>> methodToOriginalParamsMap = new HashMap<JMethod, ArrayList<JParameter>>();
873:            private final boolean saveCodeGenTypes;
874:            private JMethod stringValueOfChar = null;
875:
876:            private Pruner(JProgram program, boolean saveCodeGenTypes) {
877:                this .program = program;
878:                this .saveCodeGenTypes = saveCodeGenTypes;
879:            }
880:
881:            private boolean execImpl() {
882:                boolean madeChanges = false;
883:                while (true) {
884:                    RescueVisitor rescuer = new RescueVisitor();
885:                    // Always rescue and instantiate the "base" array type
886:                    rescuer.rescue(program.getIndexedType("Array"), true, true);
887:                    for (JReferenceType type : program.codeGenTypes) {
888:                        rescuer.rescue(type, true, saveCodeGenTypes);
889:                    }
890:                    for (JMethod method : program.entryMethods) {
891:                        rescuer.rescue(method);
892:                    }
893:
894:                    UpRefVisitor upRefer = new UpRefVisitor(rescuer);
895:                    do {
896:                        rescuer.commitInstantiatedTypes();
897:                        upRefer.accept(program);
898:                    } while (upRefer.didRescue());
899:
900:                    PruneVisitor pruner = new PruneVisitor();
901:                    pruner.accept(program);
902:                    if (!pruner.didChange()) {
903:                        break;
904:                    }
905:
906:                    CleanupRefsVisitor cleaner = new CleanupRefsVisitor();
907:                    cleaner.accept(program);
908:
909:                    referencedTypes.clear();
910:                    referencedNonTypes.clear();
911:                    methodToOriginalParamsMap.clear();
912:                    madeChanges = true;
913:                }
914:                return madeChanges;
915:            }
916:
917:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.