KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > jjs > JavaToJavaScriptCompiler


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;
17
18 import com.google.gwt.core.ext.TreeLogger;
19 import com.google.gwt.core.ext.UnableToCompleteException;
20 import com.google.gwt.core.ext.typeinfo.CompilationUnitProvider;
21 import com.google.gwt.dev.jdt.ICompilationUnitAdapter;
22 import com.google.gwt.dev.jdt.RebindOracle;
23 import com.google.gwt.dev.jdt.RebindPermutationOracle;
24 import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
25 import com.google.gwt.dev.jjs.InternalCompilerException.NodeInfo;
26 import com.google.gwt.dev.jjs.ast.JClassType;
27 import com.google.gwt.dev.jjs.ast.JExpression;
28 import com.google.gwt.dev.jjs.ast.JMethod;
29 import com.google.gwt.dev.jjs.ast.JMethodCall;
30 import com.google.gwt.dev.jjs.ast.JNewInstance;
31 import com.google.gwt.dev.jjs.ast.JProgram;
32 import com.google.gwt.dev.jjs.ast.JReferenceType;
33 import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
34 import com.google.gwt.dev.jjs.impl.AssertionRemover;
35 import com.google.gwt.dev.jjs.impl.BuildTypeMap;
36 import com.google.gwt.dev.jjs.impl.CastNormalizer;
37 import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
38 import com.google.gwt.dev.jjs.impl.CompoundAssignmentNormalizer;
39 import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
40 import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
41 import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
42 import com.google.gwt.dev.jjs.impl.JavaScriptObjectCaster;
43 import com.google.gwt.dev.jjs.impl.MakeCallsStatic;
44 import com.google.gwt.dev.jjs.impl.MethodAndClassFinalizer;
45 import com.google.gwt.dev.jjs.impl.MethodCallTightener;
46 import com.google.gwt.dev.jjs.impl.MethodInliner;
47 import com.google.gwt.dev.jjs.impl.Pruner;
48 import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
49 import com.google.gwt.dev.jjs.impl.TypeMap;
50 import com.google.gwt.dev.jjs.impl.TypeTightener;
51 import com.google.gwt.dev.js.JsNormalizer;
52 import com.google.gwt.dev.js.JsObfuscateNamer;
53 import com.google.gwt.dev.js.JsPrettyNamer;
54 import com.google.gwt.dev.js.JsSourceGenerationVisitor;
55 import com.google.gwt.dev.js.JsSymbolResolver;
56 import com.google.gwt.dev.js.JsVerboseNamer;
57 import com.google.gwt.dev.js.ast.JsProgram;
58 import com.google.gwt.dev.util.DefaultTextOutput;
59 import com.google.gwt.dev.util.Util;
60
61 import org.eclipse.jdt.core.compiler.IProblem;
62 import org.eclipse.jdt.internal.compiler.CompilationResult;
63 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
64 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
65
66 import java.util.HashSet JavaDoc;
67 import java.util.Iterator JavaDoc;
68 import java.util.List JavaDoc;
69 import java.util.Set JavaDoc;
70
71 /**
72  * Compiles the Java <code>JProgram</code> representation into its
73  * corresponding JavaScript source.
74  */

75 public class JavaToJavaScriptCompiler {
76
77   private static void findEntryPoints(TreeLogger logger,
78       RebindOracle rebindOracle, String JavaDoc[] mainClassNames, JProgram program)
79       throws UnableToCompleteException {
80     JMethod bootStrapMethod = program.createMethod(null, "init".toCharArray(),
81         null, program.getTypeVoid(), false, true, true, false, false);
82     bootStrapMethod.freezeParamTypes();
83
84     for (int i = 0; i < mainClassNames.length; ++i) {
85       String JavaDoc mainClassName = mainClassNames[i];
86       JReferenceType referenceType = program.getFromTypeMap(mainClassName);
87
88       if (referenceType == null) {
89         logger.log(TreeLogger.ERROR,
90             "Could not find module entry point class '" + mainClassName + "'",
91             null);
92         throw new UnableToCompleteException();
93       }
94
95       JExpression qualifier = null;
96       JMethod mainMethod = findMainMethod(referenceType);
97       if (mainMethod == null || !mainMethod.isStatic()) {
98         // Couldn't find a static main method; must rebind the class
99
String JavaDoc originalClassName = mainClassName;
100         mainClassName = rebindOracle.rebind(logger, originalClassName);
101         referenceType = program.getFromTypeMap(mainClassName);
102         if (referenceType == null) {
103           logger.log(TreeLogger.ERROR,
104               "Could not find module entry point class '" + mainClassName
105                   + "' after rebinding from '" + originalClassName + "'", null);
106           throw new UnableToCompleteException();
107         }
108
109         if (!(referenceType instanceof JClassType)) {
110           logger.log(TreeLogger.ERROR, "Module entry point class '"
111               + mainClassName + "' must be a class", null);
112           throw new UnableToCompleteException();
113         }
114
115         JClassType mainClass = (JClassType) referenceType;
116         if (mainClass.isAbstract()) {
117           logger.log(TreeLogger.ERROR, "Module entry point class '"
118               + mainClassName + "' must not be abstract", null);
119           throw new UnableToCompleteException();
120         }
121
122         mainMethod = findMainMethodRecurse(referenceType);
123         if (mainMethod == null) {
124           logger.log(TreeLogger.ERROR,
125               "Could not find entry method 'onModuleLoad()' method in entry point class '"
126                   + mainClassName + "'", null);
127           throw new UnableToCompleteException();
128         }
129
130         if (mainMethod.isAbstract()) {
131           logger.log(TreeLogger.ERROR,
132               "Entry method 'onModuleLoad' in entry point class '"
133                   + mainClassName + "' must not be abstract", null);
134           throw new UnableToCompleteException();
135         }
136
137         if (!mainMethod.isStatic()) {
138           // Find the appropriate (noArg) constructor
139
JMethod noArgCtor = null;
140           for (int j = 0; j < mainClass.methods.size(); ++j) {
141             JMethod ctor = (JMethod) mainClass.methods.get(j);
142             if (ctor.getName().equals(mainClass.getShortName())) {
143               if (ctor.params.size() == 0) {
144                 noArgCtor = ctor;
145               }
146             }
147           }
148           if (noArgCtor == null) {
149             logger.log(
150                 TreeLogger.ERROR,
151                 "No default (zero argument) constructor could be found in entry point class '"
152                     + mainClassName
153                     + "' to qualify a call to non-static entry method 'onModuleLoad'",
154                 null);
155             throw new UnableToCompleteException();
156           }
157
158           // Construct a new instance of the class to qualify the non-static
159
// call
160
JNewInstance newInstance = new JNewInstance(program, null, mainClass);
161           qualifier = new JMethodCall(program, null, newInstance, noArgCtor);
162         }
163       }
164
165       // qualifier will be null if onModuleLoad is static
166
JMethodCall onModuleLoadCall = new JMethodCall(program, null, qualifier,
167           mainMethod);
168       bootStrapMethod.body.statements.add(onModuleLoadCall.makeStatement());
169     }
170     program.addEntryMethod(bootStrapMethod);
171   }
172
173   private static JMethod findMainMethod(JReferenceType referenceType) {
174     for (int j = 0; j < referenceType.methods.size(); ++j) {
175       JMethod method = (JMethod) referenceType.methods.get(j);
176       if (method.getName().equals("onModuleLoad")) {
177         if (method.params.size() == 0) {
178           return method;
179         }
180       }
181     }
182     return null;
183   }
184
185   private static JMethod findMainMethodRecurse(JReferenceType referenceType) {
186     for (JReferenceType it = referenceType; it != null; it = it.extnds) {
187       JMethod result = findMainMethod(it);
188       if (result != null) {
189         return result;
190       }
191     }
192     return null;
193   }
194
195   private final String JavaDoc[] declEntryPoints;
196   private final CompilationUnitDeclaration[] goldenCuds;
197   private long lastModified;
198   private final boolean obfuscate;
199   private final boolean prettyNames;
200   private final Set JavaDoc/* <IProblem> */problemSet = new HashSet JavaDoc/* <IProblem> */();
201
202   public JavaToJavaScriptCompiler(final TreeLogger logger,
203       final WebModeCompilerFrontEnd compiler, final String JavaDoc[] declEntryPts)
204       throws UnableToCompleteException {
205     this(logger, compiler, declEntryPts, true, false);
206   }
207
208   public JavaToJavaScriptCompiler(final TreeLogger logger,
209       final WebModeCompilerFrontEnd compiler, final String JavaDoc[] declEntryPts,
210       boolean obfuscate, boolean prettyNames) throws UnableToCompleteException {
211
212     if (declEntryPts.length == 0) {
213       throw new IllegalArgumentException JavaDoc("entry point(s) required");
214     }
215
216     // Remember these for subsequent compiles.
217
//
218
this.declEntryPoints = declEntryPts;
219
220     // Should we obfuscate or, if not, use pretty names?
221
//
222
this.obfuscate = obfuscate;
223     this.prettyNames = prettyNames;
224
225     // Find all the possible rebound entry points.
226
//
227
RebindPermutationOracle rpo = compiler.getRebindPermutationOracle();
228     Set JavaDoc allEntryPoints = new HashSet JavaDoc();
229     for (int i = 0; i < declEntryPts.length; i++) {
230       String JavaDoc[] all = rpo.getAllPossibleRebindAnswers(logger, declEntryPts[i]);
231       Util.addAll(allEntryPoints, all);
232     }
233     String JavaDoc[] entryPts = Util.toStringArray(allEntryPoints);
234
235     // Add intrinsics needed for code generation.
236
//
237
int k = entryPts.length;
238     String JavaDoc[] seedTypeNames = new String JavaDoc[k + 3];
239     System.arraycopy(entryPts, 0, seedTypeNames, 0, k);
240     seedTypeNames[k++] = "com.google.gwt.lang.Array";
241     seedTypeNames[k++] = "com.google.gwt.lang.Cast";
242     seedTypeNames[k++] = "com.google.gwt.lang.Exceptions";
243
244     // Compile the source and get the compiler so we can get the parse tree
245
//
246
goldenCuds = compiler.getCompilationUnitDeclarations(logger, seedTypeNames);
247
248     // Check for compilation problems. We don't log here because any problems
249
// found here will have already been logged by AbstractCompiler.
250
//
251
checkForErrors(logger, false);
252
253     // Find the newest of all these.
254
//
255
lastModified = 0;
256     CompilationUnitProvider newestCup = null;
257     for (int i = 0; i < goldenCuds.length; i++) {
258       CompilationUnitDeclaration cud = goldenCuds[i];
259       ICompilationUnitAdapter icua = (ICompilationUnitAdapter) cud.compilationResult.compilationUnit;
260       CompilationUnitProvider cup = icua.getCompilationUnitProvider();
261       long cupLastModified = cup.getLastModified();
262       if (cupLastModified > lastModified) {
263         newestCup = cup;
264         lastModified = cupLastModified;
265       }
266     }
267     if (newestCup != null) {
268       String JavaDoc loc = newestCup.getLocation();
269       String JavaDoc msg = "Newest compilation unit is '" + loc + "'";
270       logger.log(TreeLogger.DEBUG, msg, null);
271     }
272   }
273
274   /**
275    * Creates finished JavaScript source code from the specified Java compilation
276    * units.
277    */

278   public String JavaDoc compile(TreeLogger logger, RebindOracle rebindOracle)
279       throws UnableToCompleteException {
280
281     try {
282
283       // (1) Build a flattened map of TypeDeclarations => JType.
284
//
285

286       // Note that all reference types (even nested and local ones) are in the
287
// resulting type map. BuildTypeMap also parses all JSNI.
288
//
289
JProgram jprogram = new JProgram(logger, rebindOracle);
290       TypeMap typeMap = new TypeMap(jprogram);
291       JsProgram jsProgram = new JsProgram();
292       TypeDeclaration[] allTypeDeclarations = BuildTypeMap.exec(typeMap,
293           goldenCuds, jsProgram);
294
295       // BuildTypeMap can uncover syntactic JSNI errors; report & abort
296
//
297
checkForErrors(logger, true);
298
299       // Compute all super type/sub type info
300
jprogram.typeOracle.computeBeforeAST();
301
302       // (3) Create a normalized Java AST using our own notation.
303
//
304

305       // Create the tree from JDT
306
GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram);
307
308       // GenerateJavaAST can uncover semantic JSNI errors; report & abort
309
//
310
checkForErrors(logger, true);
311
312       // TODO: figure out how to have a debug mode.
313
boolean isDebugEnabled = false;
314       if (!isDebugEnabled) {
315         // Remove all assert statements.
316
AssertionRemover.exec(jprogram);
317       }
318
319       // Compute which classes have clinits
320
jprogram.typeOracle.computeAfterAST();
321
322       // Fix up GWT.create() into new operations
323
ReplaceRebinds.exec(jprogram);
324
325       // Rebind each entry point.
326
//
327
findEntryPoints(logger, rebindOracle, declEntryPoints, jprogram);
328
329       // (4) Optimize the normalized Java AST
330
boolean didChange;
331       do {
332         didChange = false;
333         // Remove unreferenced types, fields, methods, [params, locals]
334
didChange = Pruner.exec(jprogram, true) || didChange;
335         // finalize locals, params, fields, methods, classes
336
didChange = MethodAndClassFinalizer.exec(jprogram) || didChange;
337         // rewrite non-polymorphic calls as static calls; update all call sites
338
didChange = MakeCallsStatic.exec(jprogram) || didChange;
339
340         // type flow tightening
341
// - fields, locals based on assignment
342
// - params based on assignment and call sites
343
// - method bodies based on return statements
344
// - polymorphic methods based on return types of all implementors
345
// - optimize casts and instance of
346
didChange = TypeTightener.exec(jprogram) || didChange;
347
348         // tighten method call bindings
349
didChange = MethodCallTightener.exec(jprogram) || didChange;
350
351         // dead code removal??
352
didChange = DeadCodeElimination.exec(jprogram) || didChange;
353
354         // inlining
355
didChange = MethodInliner.exec(jprogram) || didChange;
356
357         if (didChange) {
358           // recompute clinits; some may now be empty
359
jprogram.typeOracle.recomputeClinits();
360         }
361
362         // prove that any types that have been culled from the main tree are
363
// unreferenced due to type tightening?
364
} while (didChange);
365
366       // (5) "Normalize" the high-level Java tree into a lower-level tree more
367
// suited for JavaScript code generation. Don't go reordering these
368
// willy-nilly because there are some subtle interdependencies.
369
if (isDebugEnabled) {
370         // AssertionNormalizer.exec(jprogram);
371
}
372       CatchBlockNormalizer.exec(jprogram);
373       CompoundAssignmentNormalizer.exec(jprogram);
374       JavaScriptObjectCaster.exec(jprogram);
375       CastNormalizer.exec(jprogram);
376       ArrayNormalizer.exec(jprogram);
377
378       // (6) Perform further post-normalization optimizations
379
// Prune everything
380
Pruner.exec(jprogram, false);
381
382       // (7) Generate a JavaScript code DOM from the Java type declarations
383
GenerateJavaScriptAST.exec(jprogram, jsProgram);
384
385       // (8) Fix invalid constructs created during JS AST gen
386
JsNormalizer.exec(jsProgram);
387
388       // (9) Resolve all unresolved JsNameRefs
389
JsSymbolResolver.exec(jsProgram);
390
391       // (10) Obfuscate
392
if (obfuscate) {
393         JsObfuscateNamer.exec(jsProgram);
394       } else if (prettyNames) {
395         JsPrettyNamer.exec(jsProgram);
396       } else {
397         JsVerboseNamer.exec(jsProgram);
398       }
399
400       DefaultTextOutput out = new DefaultTextOutput(obfuscate);
401       JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
402       v.accept(jsProgram);
403       return out.toString();
404     } catch (UnableToCompleteException e) {
405       // just rethrow
406
throw e;
407     } catch (InternalCompilerException e) {
408       TreeLogger topBranch = logger.branch(TreeLogger.ERROR,
409           "An internal compiler exception occurred", e);
410       List JavaDoc nodeTrace = e.getNodeTrace();
411       for (Iterator JavaDoc it = nodeTrace.iterator(); it.hasNext();) {
412         NodeInfo nodeInfo = (NodeInfo) it.next();
413         SourceInfo info = nodeInfo.getSourceInfo();
414         String JavaDoc msg;
415         if (info != null) {
416           String JavaDoc fileName = info.getFileName();
417           fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
418           fileName = fileName.substring(fileName.lastIndexOf('\\') + 1);
419           msg = "at " + fileName + "(" + info.getStartLine() + "): ";
420         } else {
421           msg = "<no source info>: ";
422         }
423
424         String JavaDoc description = nodeInfo.getDescription();
425         if (description != null) {
426           msg += description;
427         } else {
428           msg += "<no description available>";
429         }
430         TreeLogger nodeBranch = topBranch.branch(TreeLogger.ERROR, msg, null);
431         String JavaDoc className = nodeInfo.getClassName();
432         if (className != null) {
433           nodeBranch.log(TreeLogger.INFO, className, null);
434         }
435       }
436       throw new UnableToCompleteException();
437     } catch (Throwable JavaDoc e) {
438       logger.log(TreeLogger.ERROR, "Unexpected internal compiler error", e);
439       throw new UnableToCompleteException();
440     }
441   }
442
443   public long getLastModifiedTimeOfNewestCompilationUnit() {
444     return lastModified;
445   }
446
447   private void checkForErrors(final TreeLogger logger, boolean itemizeErrors)
448       throws UnableToCompleteException {
449     boolean compilationFailed = false;
450     if (goldenCuds.length == 0) {
451       compilationFailed = true;
452     }
453     for (int iCud = 0; iCud < goldenCuds.length; iCud++) {
454       CompilationUnitDeclaration cud = goldenCuds[iCud];
455       CompilationResult result = cud.compilationResult();
456       if (result.hasErrors()) {
457         compilationFailed = true;
458         // Early out if we don't need to itemize.
459
if (!itemizeErrors) {
460           break;
461         }
462         TreeLogger branch = logger.branch(TreeLogger.ERROR, "Errors in "
463             + String.valueOf(result.getFileName()), null);
464         IProblem[] errors = result.getErrors();
465         for (int i = 0; i < errors.length; i++) {
466           IProblem problem = errors[i];
467           if (problemSet.contains(problem)) {
468             continue;
469           }
470
471           problemSet.add(problem);
472
473           // Strip the initial code from each error.
474
//
475
String JavaDoc msg = problem.toString();
476           msg = msg.substring(msg.indexOf(' '));
477
478           // Append 'file (line): msg' to the error message.
479
//
480
int line = problem.getSourceLineNumber();
481           StringBuffer JavaDoc msgBuf = new StringBuffer JavaDoc();
482           msgBuf.append("Line ");
483           msgBuf.append(line);
484           msgBuf.append(": ");
485           msgBuf.append(msg);
486           branch.log(TreeLogger.ERROR, msgBuf.toString(), null);
487         }
488       }
489     }
490     if (compilationFailed) {
491       logger.log(TreeLogger.ERROR, "Cannot proceed due to previous errors",
492           null);
493       throw new UnableToCompleteException();
494     }
495   }
496 }
497
Popular Tags