KickJava   Java API By Example, From Geeks To Geeks.

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


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.InternalCompilerException;
19 import com.google.gwt.dev.jjs.ast.Context;
20 import com.google.gwt.dev.jjs.ast.HasEnclosingType;
21 import com.google.gwt.dev.jjs.ast.HasName;
22 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
23 import com.google.gwt.dev.jjs.ast.JArrayRef;
24 import com.google.gwt.dev.jjs.ast.JArrayType;
25 import com.google.gwt.dev.jjs.ast.JAssertStatement;
26 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
27 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
28 import com.google.gwt.dev.jjs.ast.JBlock;
29 import com.google.gwt.dev.jjs.ast.JBooleanLiteral;
30 import com.google.gwt.dev.jjs.ast.JBreakStatement;
31 import com.google.gwt.dev.jjs.ast.JCaseStatement;
32 import com.google.gwt.dev.jjs.ast.JCastOperation;
33 import com.google.gwt.dev.jjs.ast.JCharLiteral;
34 import com.google.gwt.dev.jjs.ast.JClassLiteral;
35 import com.google.gwt.dev.jjs.ast.JClassType;
36 import com.google.gwt.dev.jjs.ast.JConditional;
37 import com.google.gwt.dev.jjs.ast.JContinueStatement;
38 import com.google.gwt.dev.jjs.ast.JDoStatement;
39 import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
40 import com.google.gwt.dev.jjs.ast.JExpressionStatement;
41 import com.google.gwt.dev.jjs.ast.JField;
42 import com.google.gwt.dev.jjs.ast.JFieldRef;
43 import com.google.gwt.dev.jjs.ast.JFloatLiteral;
44 import com.google.gwt.dev.jjs.ast.JForStatement;
45 import com.google.gwt.dev.jjs.ast.JIfStatement;
46 import com.google.gwt.dev.jjs.ast.JInstanceOf;
47 import com.google.gwt.dev.jjs.ast.JIntLiteral;
48 import com.google.gwt.dev.jjs.ast.JInterfaceType;
49 import com.google.gwt.dev.jjs.ast.JLabel;
50 import com.google.gwt.dev.jjs.ast.JLabeledStatement;
51 import com.google.gwt.dev.jjs.ast.JLocal;
52 import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
53 import com.google.gwt.dev.jjs.ast.JLocalRef;
54 import com.google.gwt.dev.jjs.ast.JLongLiteral;
55 import com.google.gwt.dev.jjs.ast.JMethod;
56 import com.google.gwt.dev.jjs.ast.JMethodCall;
57 import com.google.gwt.dev.jjs.ast.JNewArray;
58 import com.google.gwt.dev.jjs.ast.JNewInstance;
59 import com.google.gwt.dev.jjs.ast.JNullLiteral;
60 import com.google.gwt.dev.jjs.ast.JParameter;
61 import com.google.gwt.dev.jjs.ast.JParameterRef;
62 import com.google.gwt.dev.jjs.ast.JPostfixOperation;
63 import com.google.gwt.dev.jjs.ast.JPrefixOperation;
64 import com.google.gwt.dev.jjs.ast.JProgram;
65 import com.google.gwt.dev.jjs.ast.JReferenceType;
66 import com.google.gwt.dev.jjs.ast.JReturnStatement;
67 import com.google.gwt.dev.jjs.ast.JStatement;
68 import com.google.gwt.dev.jjs.ast.JStringLiteral;
69 import com.google.gwt.dev.jjs.ast.JSwitchStatement;
70 import com.google.gwt.dev.jjs.ast.JThisRef;
71 import com.google.gwt.dev.jjs.ast.JThrowStatement;
72 import com.google.gwt.dev.jjs.ast.JTryStatement;
73 import com.google.gwt.dev.jjs.ast.JType;
74 import com.google.gwt.dev.jjs.ast.JTypeOracle;
75 import com.google.gwt.dev.jjs.ast.JUnaryOperator;
76 import com.google.gwt.dev.jjs.ast.JVisitor;
77 import com.google.gwt.dev.jjs.ast.JWhileStatement;
78 import com.google.gwt.dev.jjs.ast.js.JClassSeed;
79 import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
80 import com.google.gwt.dev.jjs.ast.js.JsniMethod;
81 import com.google.gwt.dev.jjs.ast.js.JsonArray;
82 import com.google.gwt.dev.jjs.ast.js.JsonObject;
83 import com.google.gwt.dev.jjs.ast.js.JsonObject.JsonPropInit;
84 import com.google.gwt.dev.js.ast.JsArrayAccess;
85 import com.google.gwt.dev.js.ast.JsArrayLiteral;
86 import com.google.gwt.dev.js.ast.JsBinaryOperation;
87 import com.google.gwt.dev.js.ast.JsBinaryOperator;
88 import com.google.gwt.dev.js.ast.JsBlock;
89 import com.google.gwt.dev.js.ast.JsBreak;
90 import com.google.gwt.dev.js.ast.JsCase;
91 import com.google.gwt.dev.js.ast.JsCatch;
92 import com.google.gwt.dev.js.ast.JsCollection;
93 import com.google.gwt.dev.js.ast.JsConditional;
94 import com.google.gwt.dev.js.ast.JsContext;
95 import com.google.gwt.dev.js.ast.JsContinue;
96 import com.google.gwt.dev.js.ast.JsDefault;
97 import com.google.gwt.dev.js.ast.JsDoWhile;
98 import com.google.gwt.dev.js.ast.JsExprStmt;
99 import com.google.gwt.dev.js.ast.JsExpression;
100 import com.google.gwt.dev.js.ast.JsFor;
101 import com.google.gwt.dev.js.ast.JsFunction;
102 import com.google.gwt.dev.js.ast.JsIf;
103 import com.google.gwt.dev.js.ast.JsIntegralLiteral;
104 import com.google.gwt.dev.js.ast.JsInvocation;
105 import com.google.gwt.dev.js.ast.JsLabel;
106 import com.google.gwt.dev.js.ast.JsModVisitor;
107 import com.google.gwt.dev.js.ast.JsName;
108 import com.google.gwt.dev.js.ast.JsNameRef;
109 import com.google.gwt.dev.js.ast.JsNew;
110 import com.google.gwt.dev.js.ast.JsNode;
111 import com.google.gwt.dev.js.ast.JsObjectLiteral;
112 import com.google.gwt.dev.js.ast.JsParameter;
113 import com.google.gwt.dev.js.ast.JsParameters;
114 import com.google.gwt.dev.js.ast.JsPostfixOperation;
115 import com.google.gwt.dev.js.ast.JsPrefixOperation;
116 import com.google.gwt.dev.js.ast.JsProgram;
117 import com.google.gwt.dev.js.ast.JsPropertyInitializer;
118 import com.google.gwt.dev.js.ast.JsReturn;
119 import com.google.gwt.dev.js.ast.JsScope;
120 import com.google.gwt.dev.js.ast.JsStatement;
121 import com.google.gwt.dev.js.ast.JsStatements;
122 import com.google.gwt.dev.js.ast.JsStringLiteral;
123 import com.google.gwt.dev.js.ast.JsSwitch;
124 import com.google.gwt.dev.js.ast.JsSwitchMember;
125 import com.google.gwt.dev.js.ast.JsThisRef;
126 import com.google.gwt.dev.js.ast.JsThrow;
127 import com.google.gwt.dev.js.ast.JsTry;
128 import com.google.gwt.dev.js.ast.JsUnaryOperation;
129 import com.google.gwt.dev.js.ast.JsUnaryOperator;
130 import com.google.gwt.dev.js.ast.JsVars;
131 import com.google.gwt.dev.js.ast.JsWhile;
132 import com.google.gwt.dev.js.ast.JsVars.JsVar;
133
134 import java.math.BigInteger JavaDoc;
135 import java.util.ArrayList JavaDoc;
136 import java.util.Arrays JavaDoc;
137 import java.util.Collections JavaDoc;
138 import java.util.HashSet JavaDoc;
139 import java.util.IdentityHashMap JavaDoc;
140 import java.util.Iterator JavaDoc;
141 import java.util.List JavaDoc;
142 import java.util.Map JavaDoc;
143 import java.util.Set JavaDoc;
144 import java.util.Stack JavaDoc;
145 import java.util.TreeMap JavaDoc;
146 import java.util.Map.Entry;
147
148 /**
149  * Creates a JavaScript AST from a <code>JProgram</code> node.
150  */

151 public class GenerateJavaScriptAST {
152
153   private class CreateNamesAndScopesVisitor extends JVisitor {
154
155     private final Stack JavaDoc/* <JsScope> */scopeStack = new Stack JavaDoc();
156
157     // @Override
158
public void endVisit(JClassType x, Context ctx) {
159       pop();
160     }
161
162     // @Override
163
public void endVisit(JField x, Context ctx) {
164       String JavaDoc name = x.getName();
165       String JavaDoc mangleName = mangleName(x);
166       if (x.isStatic()) {
167         names.put(x, topScope.declareName(mangleName, name));
168       } else {
169         names.put(x, peek().declareName(mangleName, name));
170       }
171     }
172
173     // @Override
174
public void endVisit(JInterfaceType x, Context ctx) {
175       pop();
176     }
177
178     // @Override
179
public void endVisit(JLabel x, Context ctx) {
180       if (getName(x) != null) {
181         return;
182       }
183       names.put(x, peek().declareName(x.getName()));
184     }
185
186     // @Override
187
public void endVisit(JLocal x, Context ctx) {
188       // locals can conflict, that's okay just reuse the same variable
189
JsScope scope = peek();
190       JsName jsName = scope.declareName(x.getName());
191       names.put(x, jsName);
192     }
193
194     // @Override
195
public void endVisit(JMethod x, Context ctx) {
196       pop();
197     }
198
199     // @Override
200
public void endVisit(JParameter x, Context ctx) {
201       names.put(x, peek().declareName(x.getName()));
202     }
203
204     // @Override
205
public void endVisit(JProgram x, Context ctx) {
206       // visit special things that may have been culled
207
JField field = x.getSpecialField("Object.typeId");
208       names.put(field, objectScope.declareName(mangleName(field),
209           field.getName()));
210
211       field = x.getSpecialField("Object.typeName");
212       names.put(field, objectScope.declareName(mangleName(field),
213           field.getName()));
214
215       field = x.getSpecialField("Cast.typeIdArray");
216       names.put(field, topScope.declareName(mangleName(field), field.getName()));
217
218       /*
219        * put the null method and field into objectScope since they can be
220        * referenced as instance on null-types (as determined by type flow)
221        */

222       JMethod nullMethod = x.getNullMethod();
223       polymorphicNames.put(nullMethod,
224           objectScope.declareName(nullMethod.getName()));
225       JField nullField = x.getNullField();
226       JsName nullFieldName = objectScope.declareName(nullField.getName());
227       polymorphicNames.put(nullField, nullFieldName);
228       names.put(nullField, nullFieldName);
229
230       /*
231        * put nullMethod in the global scope, too; it's the replacer for clinits
232        */

233       nullMethodName = topScope.declareName(nullMethod.getName());
234       names.put(nullMethod, nullMethodName);
235     }
236
237     // @Override
238
public void endVisit(JsniMethod x, Context ctx) {
239       // didn't push anything
240
}
241
242     // @Override
243
public boolean visit(JClassType x, Context ctx) {
244       // have I already been visited as a super type?
245
JsScope myScope = (JsScope) classScopes.get(x);
246       if (myScope != null) {
247         push(myScope);
248         return false;
249       }
250
251       // My seed function name
252
names.put(x, topScope.declareName(getNameString(x), x.getShortName()));
253
254       // My class scope
255
if (x.extnds == null) {
256         myScope = objectScope;
257       } else {
258         JsScope parentScope = (JsScope) classScopes.get(x.extnds);
259         // Run my superclass first!
260
if (parentScope == null) {
261           accept(x.extnds);
262         }
263         parentScope = (JsScope) classScopes.get(x.extnds);
264         assert (parentScope != null);
265         /*
266          * WEIRD: we wedge the global interface scope in between object and all
267          * of its subclasses; this ensures that interface method names trump all
268          * (except Object method names)
269          */

270         if (parentScope == objectScope) {
271           parentScope = interfaceScope;
272         }
273         myScope = new JsScope(parentScope, "class " + x.getShortName());
274       }
275       classScopes.put(x, myScope);
276
277       // Make a name for my package if it doesn't already exist
278
String JavaDoc packageName = getPackageName(x.getName());
279       if (packageName != null && packageName.length() > 0) {
280         if (packageNames.get(packageName) == null) {
281           String JavaDoc ident = "package_" + packageName.replace('.', '_');
282           JsName jsName = topScope.declareName(ident);
283           packageNames.put(packageName, jsName);
284         }
285       }
286
287       push(myScope);
288       return true;
289     }
290
291     // @Override
292
public boolean visit(JInterfaceType x, Context ctx) {
293       // interfaces have no name at run time
294
push(interfaceScope);
295       return true;
296     }
297
298     // @Override
299
public boolean visit(JMethod x, Context ctx) {
300
301       // my polymorphic name
302
String JavaDoc name = x.getName();
303       if (!x.isStatic()) {
304         if (getPolyName(x) == null) {
305           String JavaDoc mangleName = mangleNameForPoly(x);
306           JsName polyName = interfaceScope.declareName(mangleName, name);
307           polymorphicNames.put(x, polyName);
308         }
309       }
310
311       if (x.isAbstract()) {
312         // just push a dummy scope that we can pop in endVisit
313
push(null);
314         return false;
315       }
316
317       // my global name
318
JsName globalName;
319       if (x.getEnclosingType() == null) {
320         globalName = topScope.declareName(name);
321       } else {
322         String JavaDoc mangleName = mangleNameForGlobal(x);
323         globalName = topScope.declareName(mangleName, name);
324       }
325       names.put(x, globalName);
326
327       // create my peer JsFunction
328
JsFunction jsFunction = new JsFunction(topScope, globalName);
329       methodMap.put(x, jsFunction);
330
331       push(jsFunction.getScope());
332       return true;
333     }
334
335     // @Override
336
public boolean visit(JsniMethod x, Context ctx) {
337       // my polymorphic name
338
String JavaDoc name = x.getName();
339       if (!x.isStatic()) {
340         if (getPolyName(x) == null) {
341           String JavaDoc mangleName = mangleNameForPoly(x);
342           JsName polyName = interfaceScope.declareName(mangleName, name);
343           polymorphicNames.put(x, polyName);
344         }
345       }
346
347       // set my global name now that we have a name allocator
348
String JavaDoc fnName = mangleNameForGlobal(x);
349       JsName globalName = topScope.declareName(fnName, name);
350       x.getFunc().setName(globalName);
351       names.put(x, globalName);
352
353       return false;
354     }
355
356     public boolean visit(JTryStatement x, Context ctx) {
357       accept(x.getTryBlock());
358
359       List JavaDoc catchArgs = x.getCatchArgs();
360       List JavaDoc catchBlocks = x.getCatchBlocks();
361       for (int i = 0, c = catchArgs.size(); i < c; ++i) {
362         JLocalRef arg = (JLocalRef) catchArgs.get(i);
363         JBlock catchBlock = (JBlock) catchBlocks.get(i);
364         JsCatch jsCatch = new JsCatch(peek(), arg.getTarget().getName());
365         JsParameter jsParam = jsCatch.getParameter();
366         names.put(arg.getTarget(), jsParam.getName());
367         catchMap.put(catchBlock, jsCatch);
368
369         push(jsCatch.getScope());
370         accept(catchBlock);
371         pop();
372       }
373
374       // TODO: normalize this so it's never null?
375
if (x.getFinallyBlock() != null) {
376         accept(x.getFinallyBlock());
377       }
378       return false;
379     }
380
381     private JsScope peek() {
382       return (JsScope) scopeStack.peek();
383     }
384
385     private void pop() {
386       scopeStack.pop();
387     }
388
389     private void push(JsScope scope) {
390       scopeStack.push(scope);
391     }
392   }
393
394   private class GenerateJavaScriptVisitor extends JVisitor {
395
396     private final Set JavaDoc/* <JClassType> */alreadyRan = new HashSet JavaDoc/* <JClassType> */();
397
398     private JMethod currentMethod = null;
399
400     private final JsName globalTemp = topScope.declareName("_");
401
402     private final Stack JavaDoc/* <JsNode> */nodeStack = new Stack JavaDoc/* <JsNode> */();
403
404     private final JsName prototype = objectScope.declareName("prototype");
405
406     {
407       globalTemp.setObfuscatable(false);
408       prototype.setObfuscatable(false);
409     }
410
411     // @Override
412
public void endVisit(JAbsentArrayDimension x, Context ctx) {
413       throw new InternalCompilerException("Should not get here.");
414     }
415
416     // @Override
417
public void endVisit(JArrayRef x, Context ctx) {
418       JsArrayAccess jsArrayAccess = new JsArrayAccess();
419       jsArrayAccess.setIndexExpr((JsExpression) pop());
420       jsArrayAccess.setArrayExpr((JsExpression) pop());
421       push(jsArrayAccess);
422     }
423
424     // @Override
425
public void endVisit(JAssertStatement x, Context ctx) {
426       throw new InternalCompilerException("Should not get here.");
427     }
428
429     // @Override
430
public void endVisit(JBinaryOperation x, Context ctx) {
431       JsExpression rhs = (JsExpression) pop(); // rhs
432
JsExpression lhs = (JsExpression) pop(); // lhs
433
JsBinaryOperator myOp = JavaToJsOperatorMap.get(x.getOp());
434
435       /*
436        * Use === and !== on reference types, or else you can get wrong answers
437        * when Object.toString() == 'some string'.
438        */

439       if (myOp == JsBinaryOperator.EQ
440           && x.getLhs().getType() instanceof JReferenceType
441           && x.getRhs().getType() instanceof JReferenceType) {
442         myOp = JsBinaryOperator.REF_EQ;
443       } else if (myOp == JsBinaryOperator.NEQ
444           && x.getLhs().getType() instanceof JReferenceType
445           && x.getRhs().getType() instanceof JReferenceType) {
446         myOp = JsBinaryOperator.REF_NEQ;
447       }
448
449       push(new JsBinaryOperation(myOp, lhs, rhs));
450     }
451
452     // @Override
453
public void endVisit(JBlock x, Context ctx) {
454       JsBlock jsBlock = new JsBlock();
455       JsStatements stmts = jsBlock.getStatements();
456       popList(stmts, x.statements.size()); // stmts
457
Iterator JavaDoc iterator = stmts.iterator();
458       while (iterator.hasNext()) {
459         JsStatement stmt = (JsStatement) iterator.next();
460         if (stmt == jsProgram.getEmptyStmt()) {
461           iterator.remove();
462         }
463       }
464       push(jsBlock);
465     }
466
467     // @Override
468
public void endVisit(JBooleanLiteral x, Context ctx) {
469       push(x.getValue() ? jsProgram.getTrueLiteral()
470           : jsProgram.getFalseLiteral());
471     }
472
473     // @Override
474
public void endVisit(JBreakStatement x, Context ctx) {
475       JsNameRef labelRef = null;
476       if (x.getLabel() != null) {
477         JsLabel label = (JsLabel) pop(); // label
478
labelRef = label.getName().makeRef();
479       }
480       push(new JsBreak(labelRef));
481     }
482
483     // @Override
484
public void endVisit(JCaseStatement x, Context ctx) {
485       if (x.getExpr() == null) {
486         push(new JsDefault());
487       } else {
488         JsCase jsCase = new JsCase();
489         jsCase.setCaseExpr((JsExpression) pop()); // expr
490
push(jsCase);
491       }
492     }
493
494     // @Override
495
public void endVisit(JCastOperation x, Context ctx) {
496       throw new InternalCompilerException("Should not get here.");
497     }
498
499     // @Override
500
public void endVisit(JCharLiteral x, Context ctx) {
501       push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
502     }
503
504     // @Override
505
public void endVisit(JClassLiteral x, Context ctx) {
506       // My seed function name
507
String JavaDoc nameString = x.getRefType().getJavahSignatureName() + "_classlit";
508       JsName classLit = topScope.declareName(nameString);
509       classLits.put(x.getRefType(), classLit);
510       push(classLit.makeRef());
511     }
512
513     // @Override
514
public void endVisit(JClassSeed x, Context ctx) {
515       push(getName(x.getRefType()).makeRef());
516     }
517
518     // @Override
519
public void endVisit(JClassType x, Context ctx) {
520       if (alreadyRan.contains(x)) {
521         return;
522       }
523
524       alreadyRan.add(x);
525
526       List JavaDoc/* <JsFunction> */jsFuncs = popList(x.methods.size()); // methods
527
List JavaDoc/* <JsStatement> */jsFields = popList(x.fields.size()); // fields
528

529       if (typeOracle.hasDirectClinit(x)) {
530         // see if there's a super class we need to chain to
531
JClassType superType = x.extnds;
532         while (superType != null && !typeOracle.hasDirectClinit(superType)) {
533           superType = superType.extnds;
534         }
535         handleClinit((JsFunction) jsFuncs.get(0), superType);
536       } else {
537         jsFuncs.set(0, null);
538       }
539
540       JsStatements globalStmts = jsProgram.getGlobalBlock().getStatements();
541
542       // declare all methods into the global scope
543
for (int i = 0; i < jsFuncs.size(); ++i) {
544         JsFunction func = (JsFunction) jsFuncs.get(i);
545         if (func != null) {
546           globalStmts.add(func.makeStmt());
547         }
548       }
549
550       if (typeOracle.isInstantiatedType(x)) {
551         generateClassSetup(x, globalStmts);
552       }
553
554       // setup fields
555
JsVars vars = new JsVars();
556       for (int i = 0; i < jsFields.size(); ++i) {
557         JsNode node = (JsNode) jsFields.get(i);
558         if (node instanceof JsVar) {
559           vars.add((JsVar) node);
560         } else {
561           assert (node instanceof JsStatement);
562           JsStatement stmt = (JsStatement) jsFields.get(i);
563           globalStmts.add(stmt);
564         }
565       }
566       if (!vars.isEmpty()) {
567         globalStmts.add(vars);
568       }
569     }
570
571     // @Override
572
public void endVisit(JConditional x, Context ctx) {
573       JsExpression elseExpr = (JsExpression) pop(); // elseExpr
574
JsExpression thenExpr = (JsExpression) pop(); // thenExpr
575
JsExpression ifTest = (JsExpression) pop(); // ifTest
576
push(new JsConditional(ifTest, thenExpr, elseExpr));
577     }
578
579     // @Override
580
public void endVisit(JContinueStatement x, Context ctx) {
581       JsNameRef labelRef = null;
582       if (x.getLabel() != null) {
583         JsLabel label = (JsLabel) pop(); // label
584
labelRef = label.getName().makeRef();
585       }
586       push(new JsContinue(labelRef));
587     }
588
589     // @Override
590
public void endVisit(JDoStatement x, Context ctx) {
591       JsDoWhile stmt = new JsDoWhile();
592       if (x.getBody() != null) {
593         stmt.setBody((JsStatement) pop()); // body
594
} else {
595         stmt.setBody(jsProgram.getEmptyStmt());
596       }
597       stmt.setCondition((JsExpression) pop()); // testExpr
598
push(stmt);
599     }
600
601     // @Override
602
public void endVisit(JDoubleLiteral x, Context ctx) {
603       push(jsProgram.getDecimalLiteral(String.valueOf(x.getValue())));
604     }
605
606     // @Override
607
public void endVisit(JExpressionStatement x, Context ctx) {
608       JsExpression expr = (JsExpression) pop(); // expr
609
push(expr.makeStmt());
610     }
611
612     // @Override
613
public void endVisit(JField x, Context ctx) {
614       // if we need an initial value, create an assignment
615
if (x.constInitializer != null) {
616         // setup the constant value
617
accept(x.constInitializer);
618       } else if (!x.hasInitializer()) {
619         // setup a default value
620
accept(x.getType().getDefaultValue());
621       } else {
622         // the variable is setup during clinit, no need to initialize here
623
push(null);
624       }
625       JsExpression rhs = (JsExpression) pop();
626       JsName name = getName(x);
627
628       if (x.isStatic()) {
629         // setup a var for the static
630
JsVar var = new JsVar(name);
631         var.setInitExpr(rhs);
632         push(var);
633       } else {
634         // for non-statics, only setup an assignment if needed
635
if (rhs != null) {
636           JsNameRef fieldRef = name.makeRef();
637           fieldRef.setQualifier(globalTemp.makeRef());
638           JsExpression asg = createAssignment(fieldRef, rhs);
639           push(new JsExprStmt(asg));
640         } else {
641           push(null);
642         }
643       }
644     }
645
646     // @Override
647
public void endVisit(JFieldRef x, Context ctx) {
648       JField field = x.getField();
649       JsName jsFieldName = getName(field);
650       JsNameRef nameRef = jsFieldName.makeRef();
651       JsExpression curExpr = nameRef;
652
653       /*
654        * Note: the comma expressions here would cause an illegal tree state if
655        * the result expression ended up on the lhs of an assignment. A hack in
656        * in endVisit(JBinaryOperation) rectifies the situation.
657        */

658
659       // See if we need a clinit
660
JsInvocation jsInvocation = maybeCreateClinitCall(field);
661       if (jsInvocation != null) {
662         curExpr = createCommaExpression(jsInvocation, curExpr);
663       }
664
665       if (x.getInstance() != null) {
666         JsExpression qualifier = (JsExpression) pop();
667         if (field.isStatic()) {
668           // unnecessary qualifier, create a comma expression
669
curExpr = createCommaExpression(qualifier, curExpr);
670         } else {
671           // necessary qualifier, qualify the name ref
672
nameRef.setQualifier(qualifier);
673         }
674       }
675
676       push(curExpr);
677     }
678
679     // @Override
680
public void endVisit(JFloatLiteral x, Context ctx) {
681       push(jsProgram.getDecimalLiteral(String.valueOf(x.getValue())));
682     }
683
684     // @Override
685
public void endVisit(JForStatement x, Context ctx) {
686       JsFor jsFor = new JsFor();
687
688       // body
689
if (x.getBody() != null) {
690         jsFor.setBody((JsStatement) pop());
691       } else {
692         jsFor.setBody(jsProgram.getEmptyStmt());
693       }
694
695       // increments
696
{
697         JsExpression incrExpr = null;
698         List JavaDoc/* <JsExprStmt> */exprStmts = popList(x.getIncrements().size());
699         for (int i = 0; i < exprStmts.size(); ++i) {
700           JsExprStmt exprStmt = (JsExprStmt) exprStmts.get(i);
701           incrExpr = createCommaExpression(incrExpr, exprStmt.getExpression());
702         }
703         jsFor.setIncrExpr(incrExpr);
704       }
705
706       // condition
707
if (x.getTestExpr() != null) {
708         jsFor.setCondition((JsExpression) pop());
709       }
710
711       // initializers
712
JsExpression initExpr = null;
713       List JavaDoc/* <JsExprStmt> */initStmts = popList(x.getInitializers().size());
714       for (int i = 0; i < initStmts.size(); ++i) {
715         JsExprStmt initStmt = (JsExprStmt) initStmts.get(i);
716         if (initStmt != null) {
717           initExpr = createCommaExpression(initExpr, initStmt.getExpression());
718         }
719       }
720       jsFor.setInitExpr(initExpr);
721
722       push(jsFor);
723     }
724
725     // @Override
726
public void endVisit(JIfStatement x, Context ctx) {
727       JsIf stmt = new JsIf();
728
729       if (x.getElseStmt() != null) {
730         stmt.setElseStmt((JsStatement) pop()); // elseStmt
731
}
732
733       if (x.getThenStmt() != null) {
734         stmt.setThenStmt((JsStatement) pop()); // thenStmt
735
} else {
736         stmt.setThenStmt(jsProgram.getEmptyStmt());
737       }
738
739       stmt.setIfExpr((JsExpression) pop()); // ifExpr
740
push(stmt);
741     }
742
743     // @Override
744
public void endVisit(JInstanceOf x, Context ctx) {
745       throw new InternalCompilerException("Should not get here.");
746     }
747
748     // @Override
749
public void endVisit(JInterfaceType x, Context ctx) {
750       List JavaDoc/* <JsFunction> */jsFuncs = popList(x.methods.size()); // methods
751
List JavaDoc/* <JsStatement> */jsFields = popList(x.fields.size()); // fields
752

753       JsStatements globalStmts = jsProgram.getGlobalBlock().getStatements();
754
755       if (typeOracle.hasDirectClinit(x)) {
756         JsFunction clinitFunc = (JsFunction) jsFuncs.get(0);
757         handleClinit(clinitFunc, null);
758         globalStmts.add(clinitFunc.makeStmt());
759       }
760
761       // setup fields
762
JsVars vars = new JsVars();
763       for (int i = 0; i < jsFields.size(); ++i) {
764         JsNode node = (JsNode) jsFields.get(i);
765         assert (node instanceof JsVar);
766         vars.add((JsVar) node);
767       }
768       if (!vars.isEmpty()) {
769         globalStmts.add(vars);
770       }
771     }
772
773     // @Override
774
public void endVisit(JIntLiteral x, Context ctx) {
775       push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
776     }
777
778     // @Override
779
public void endVisit(JLabel x, Context ctx) {
780       push(new JsLabel(getName(x)));
781     }
782
783     // @Override
784
public void endVisit(JLabeledStatement x, Context ctx) {
785       JsStatement body = (JsStatement) pop(); // body
786
JsLabel label = (JsLabel) pop(); // label
787
label.setStmt(body);
788       push(label);
789     }
790
791     // @Override
792
public void endVisit(JLocal x, Context ctx) {
793       push(getName(x).makeRef());
794     }
795
796     // @Override
797
public void endVisit(JLocalDeclarationStatement x, Context ctx) {
798
799       if (x.getInitializer() == null) {
800         pop(); // localRef
801
/*
802          * local decls can only appear in blocks, so it's okay to push null
803          * instead of an empty statement
804          */

805         push(null);
806         return;
807       }
808
809       JsExpression initializer = (JsExpression) pop(); // initializer
810
JsNameRef localRef = (JsNameRef) pop(); // localRef
811

812       JsBinaryOperation binOp = new JsBinaryOperation(JsBinaryOperator.ASG,
813           localRef, initializer);
814
815       push(binOp.makeStmt());
816     }
817
818     // @Override
819
public void endVisit(JLocalRef x, Context ctx) {
820       push(getName(x.getTarget()).makeRef());
821     }
822
823     // @Override
824
public void endVisit(JLongLiteral x, Context ctx) {
825       push(jsProgram.getIntegralLiteral(BigInteger.valueOf(x.getValue())));
826     }
827
828     // @Override
829
public void endVisit(JMethod x, Context ctx) {
830
831       JsBlock body = (JsBlock) pop();
832       List JavaDoc/* <JsNameRef> */locals = popList(x.locals.size()); // locals
833
List JavaDoc/* <JsParameter> */params = popList(x.params.size()); // params
834

835       if (x.isAbstract()) {
836         push(null);
837         return;
838       }
839
840       JsFunction jsFunc = (JsFunction) methodMap.get(x);
841       jsFunc.setBody(body); // body
842

843       JsParameters jsParams = jsFunc.getParameters();
844       for (int i = 0; i < params.size(); ++i) {
845         JsParameter param = (JsParameter) params.get(i);
846         jsParams.add(param);
847       }
848
849       /*
850        * Emit a statement to declare the method's complete set of local
851        * variables. JavaScript doesn't have the same concept of lexical scoping
852        * as Java, so it's okay to just predeclare all local vars at the top of
853        * the function, which saves us having to use the "var" keyword over and
854        * over.
855        *
856        * Note: it's fine to use the same JS ident to represent two different
857        * Java locals of the same name since they could never conflict with each
858        * other in Java. We use the alreadySeen set to make sure we don't declare
859        * the same-named local var twice.
860        */

861       JsVars vars = new JsVars();
862       Set JavaDoc alreadySeen = new HashSet JavaDoc();
863       for (int i = 0; i < locals.size(); ++i) {
864         JsName name = (JsName) names.get(x.locals.get(i));
865         String JavaDoc ident = name.getIdent();
866         if (!alreadySeen.contains(ident)) {
867           alreadySeen.add(ident);
868           vars.add(new JsVar(name));
869         }
870       }
871
872       if (!vars.isEmpty()) {
873         jsFunc.getBody().getStatements().add(0, vars);
874       }
875
876       JsInvocation jsInvocation = maybeCreateClinitCall(x);
877       if (jsInvocation != null) {
878         jsFunc.getBody().getStatements().add(0, jsInvocation.makeStmt());
879       }
880
881       push(jsFunc);
882       currentMethod = null;
883     }
884
885     // @Override
886
public void endVisit(JMethodCall x, Context ctx) {
887       JMethod method = x.getTarget();
888       JsInvocation jsInvocation = new JsInvocation();
889
890       popList(jsInvocation.getArguments(), x.getArgs().size()); // args
891

892       JsNameRef qualifier;
893       JsExpression unnecessaryQualifier = null;
894       if (method.isStatic()) {
895         if (x.getInstance() != null) {
896           unnecessaryQualifier = (JsExpression) pop(); // instance
897
}
898         qualifier = getName(method).makeRef();
899       } else {
900         if (x.isStaticDispatchOnly()) {
901           /*
902            * Dispatch statically (odd case). This happens when a call that must
903            * be static is targeting an instance method that could not be
904            * transformed into a static. For example, making a super call into a
905            * native method currently causes this, because we cannot currently
906            * staticify native methods.
907            *
908            * Have to use a "call" construct.
909            */

910           JsName callName = objectScope.declareName("call");
911           callName.setObfuscatable(false);
912           qualifier = callName.makeRef();
913           qualifier.setQualifier(getName(method).makeRef());
914           jsInvocation.getArguments().add(0, (JsExpression) pop()); // instance
915
} else {
916           // Dispatch polymorphically (normal case).
917
qualifier = getPolyName(method).makeRef();
918           qualifier.setQualifier((JsExpression) pop()); // instance
919
}
920       }
921       jsInvocation.setQualifier(qualifier);
922       push(createCommaExpression(unnecessaryQualifier, jsInvocation));
923     }
924
925     // @Override
926
public void endVisit(JMultiExpression x, Context ctx) {
927       List JavaDoc/* <JsExpression> */exprs = popList(x.exprs.size());
928       JsExpression cur = null;
929       for (int i = 0; i < exprs.size(); ++i) {
930         JsExpression next = (JsExpression) exprs.get(i);
931         cur = createCommaExpression(cur, next);
932       }
933       push(cur);
934     }
935
936     // @Override
937
public void endVisit(JNewArray x, Context ctx) {
938       throw new InternalCompilerException("Should not get here.");
939     }
940
941     // @Override
942
public void endVisit(JNewInstance x, Context ctx) {
943       JsNew newOp = new JsNew();
944       JsNameRef nameRef = getName(x.getType()).makeRef();
945       newOp.setConstructorExpression(nameRef);
946       push(newOp);
947     }
948
949     // @Override
950
public void endVisit(JNullLiteral x, Context ctx) {
951       push(jsProgram.getNullLiteral());
952     }
953
954     // @Override
955
public void endVisit(JParameter x, Context ctx) {
956       push(new JsParameter(getName(x)));
957     }
958
959     // @Override
960
public void endVisit(JParameterRef x, Context ctx) {
961       push(getName(x.getTarget()).makeRef());
962     }
963
964     // @Override
965
public void endVisit(JPostfixOperation x, Context ctx) {
966       JsUnaryOperation op = new JsPostfixOperation(
967           JavaToJsOperatorMap.get(x.getOp()), ((JsExpression) pop())); // arg
968
push(op);
969     }
970
971     // @Override
972
public void endVisit(JPrefixOperation x, Context ctx) {
973       JsUnaryOperation op = new JsPrefixOperation(
974           JavaToJsOperatorMap.get(x.getOp()), ((JsExpression) pop())); // arg
975
push(op);
976     }
977
978     // @Override
979
public void endVisit(JProgram x, Context ctx) {
980       JsStatements globalStmts = jsProgram.getGlobalBlock().getStatements();
981
982       // types don't push
983

984       // Generate entry methods
985
List JavaDoc/* <JsFunction> */entryFuncs = popList(x.entryMethods.size()); // entryMethods
986
for (int i = 0; i < entryFuncs.size(); ++i) {
987         JsFunction func = (JsFunction) entryFuncs.get(i);
988         if (func != null) {
989           globalStmts.add(func.makeStmt());
990         }
991       }
992
993       generateGwtOnLoad(entryFuncs, globalStmts);
994
995       // a few more vars on the very end
996
JsVars vars = new JsVars();
997       generateTypeTable(vars);
998       generateClassLiterals(vars);
999       globalStmts.add(vars);
1000    }
1001
1002    // @Override
1003
public void endVisit(JReturnStatement x, Context ctx) {
1004      if (x.getExpr() != null) {
1005        push(new JsReturn((JsExpression) pop())); // expr
1006
} else {
1007        push(new JsReturn());
1008      }
1009    }
1010
1011    // @Override
1012
public void endVisit(JsniMethod x, Context ctx) {
1013      JsFunction jsFunc = x.getFunc();
1014
1015      // replace all JSNI idents with a real JsName now that we know it
1016
new JsModVisitor() {
1017        // @Override
1018
public void endVisit(JsNameRef x, JsContext ctx) {
1019          String JavaDoc ident = x.getIdent();
1020          if (ident.charAt(0) == '@') {
1021            HasEnclosingType node = (HasEnclosingType) program.jsniMap.get(ident);
1022            assert (node != null);
1023            if (node instanceof JField) {
1024              JField field = (JField) node;
1025              JsName jsName = getName(field);
1026              assert (jsName != null);
1027              x.resolve(jsName);
1028
1029              // See if we need to add a clinit call to a static field ref
1030
JsInvocation clinitCall = maybeCreateClinitCall(field);
1031              if (clinitCall != null) {
1032                JsExpression commaExpr = createCommaExpression(clinitCall, x);
1033                ctx.replaceMe(commaExpr);
1034              }
1035            } else {
1036              JMethod method = (JMethod) node;
1037              if (x.getQualifier() == null) {
1038                JsName jsName = getName(method);
1039                assert (jsName != null);
1040                x.resolve(jsName);
1041              } else {
1042                JsName jsName = getPolyName(method);
1043                if (jsName == null) {
1044                  // this can occur when JSNI references an instance method on a
1045
// type that was never actually instantiated.
1046
jsName = nullMethodName;
1047                }
1048                x.resolve(jsName);
1049              }
1050            }
1051          }
1052        }
1053      }.accept(jsFunc);
1054
1055      JsInvocation jsInvocation = maybeCreateClinitCall(x);
1056      if (jsInvocation != null) {
1057        jsFunc.getBody().getStatements().add(0, jsInvocation.makeStmt());
1058      }
1059
1060      push(jsFunc);
1061      currentMethod = null;
1062    }
1063
1064    // @Override
1065
public void endVisit(JsonArray x, Context ctx) {
1066      JsArrayLiteral jsArrayLiteral = new JsArrayLiteral();
1067      popList(jsArrayLiteral.getExpressions(), x.exprs.size());
1068      push(jsArrayLiteral);
1069    }
1070
1071    // @Override
1072
public void endVisit(JsonObject x, Context ctx) {
1073      JsObjectLiteral jsObjectLiteral = new JsObjectLiteral();
1074      popList(jsObjectLiteral.getPropertyInitializers(), x.propInits.size());
1075      push(jsObjectLiteral);
1076    }
1077
1078    // @Override
1079
public void endVisit(JsonPropInit init, Context ctx) {
1080      JsExpression valueExpr = (JsExpression) pop();
1081      JsExpression labelExpr = (JsExpression) pop();
1082      push(new JsPropertyInitializer(labelExpr, valueExpr));
1083    }
1084
1085    // @Override
1086
public void endVisit(JStringLiteral x, Context ctx) {
1087      push(jsProgram.getStringLiteral(x.getValue()));
1088    }
1089
1090    // @Override
1091
public void endVisit(JThisRef x, Context ctx) {
1092      push(new JsThisRef());
1093    }
1094
1095    // @Override
1096
public void endVisit(JThrowStatement x, Context ctx) {
1097      push(new JsThrow((JsExpression) pop())); // expr
1098
}
1099
1100    // @Override
1101
public void endVisit(JTryStatement x, Context ctx) {
1102      JsTry jsTry = new JsTry();
1103
1104      if (x.getFinallyBlock() != null) {
1105        JsBlock finallyBlock = (JsBlock) pop(); // finallyBlock
1106
if (finallyBlock.getStatements().size() > 0) {
1107          jsTry.setFinallyBlock(finallyBlock);
1108        }
1109      }
1110
1111      int size = x.getCatchArgs().size();
1112      assert (size < 2 && size == x.getCatchBlocks().size());
1113      if (size == 1) {
1114        JsBlock catchBlock = (JsBlock) pop(); // catchBlocks
1115
pop(); // catchArgs
1116
JsCatch jsCatch = (JsCatch) catchMap.get(x.getCatchBlocks().get(0));
1117        jsCatch.setBody(catchBlock);
1118        jsTry.getCatches().add(jsCatch);
1119      }
1120
1121      jsTry.setTryBlock((JsBlock) pop()); // tryBlock
1122

1123      push(jsTry);
1124    }
1125
1126    // @Override
1127
public void endVisit(JWhileStatement x, Context ctx) {
1128      JsWhile stmt = new JsWhile();
1129      if (x.getBody() != null) {
1130        stmt.setBody((JsStatement) pop()); // body
1131
} else {
1132        stmt.setBody(jsProgram.getEmptyStmt());
1133      }
1134      stmt.setCondition((JsExpression) pop()); // testExpr
1135
push(stmt);
1136    }
1137
1138    // @Override
1139
public boolean visit(JClassType x, Context ctx) {
1140      if (alreadyRan.contains(x)) {
1141        return false;
1142      }
1143
1144      // force super type to generate code first, this is required for prototype
1145
// chaining to work properly
1146
if (x.extnds != null && !alreadyRan.contains(x)) {
1147        accept(x.extnds);
1148      }
1149
1150      return true;
1151    }
1152
1153    // @Override
1154
public boolean visit(JMethod x, Context ctx) {
1155      currentMethod = x;
1156      return true;
1157    }
1158
1159    // @Override
1160
public boolean visit(JProgram x, Context ctx) {
1161      JsStatements globalStatements = jsProgram.getGlobalBlock().getStatements();
1162
1163      // declare some global vars
1164
JsVars vars = new JsVars();
1165
1166      // reserve the "_" identifier
1167
vars.add(new JsVar(globalTemp));
1168      generatePackageNames(vars);
1169      globalStatements.add(vars);
1170
1171      generateNullFunc(globalStatements);
1172      return true;
1173    }
1174
1175    // @Override
1176
public boolean visit(JsniMethod x, Context ctx) {
1177      currentMethod = x;
1178      return false;
1179    }
1180
1181    public boolean visit(JSwitchStatement x, Context ctx) {
1182      /*
1183       * What a pain.. JSwitchStatement and JsSwitch are modeled completely
1184       * differently. Here we try to resolve those differences.
1185       */

1186      JsSwitch jsSwitch = new JsSwitch();
1187      accept(x.getExpr());
1188      jsSwitch.setExpr((JsExpression) pop()); // expr
1189

1190      List JavaDoc/* <JStatement> */bodyStmts = x.getBody().statements;
1191      if (bodyStmts.size() > 0) {
1192        JsStatements curStatements = null;
1193        for (int i = 0; i < bodyStmts.size(); ++i) {
1194          JStatement stmt = (JStatement) bodyStmts.get(i);
1195          accept(stmt);
1196          if (stmt instanceof JCaseStatement) {
1197            // create a new switch member
1198
JsSwitchMember switchMember = (JsSwitchMember) pop(); // stmt
1199
jsSwitch.getCases().add(switchMember);
1200            curStatements = switchMember.getStmts();
1201          } else {
1202            // add to statements for current case
1203
assert (curStatements != null);
1204            JsStatement newStmt = (JsStatement) pop(); // stmt
1205
if (newStmt != null) {
1206              // Empty JLocalDeclarationStatement produces a null
1207
curStatements.add(newStmt);
1208            }
1209          }
1210        }
1211      }
1212
1213      push(jsSwitch);
1214      return false;
1215    }
1216
1217    private JsExpression createAssignment(JsExpression lhs, JsExpression rhs) {
1218      return new JsBinaryOperation(JsBinaryOperator.ASG, lhs, rhs);
1219    }
1220
1221    private JsExpression createCommaExpression(JsExpression lhs,
1222        JsExpression rhs) {
1223      if (lhs == null) {
1224        return rhs;
1225      } else if (rhs == null) {
1226        return lhs;
1227      }
1228      return new JsBinaryOperation(JsBinaryOperator.COMMA, lhs, rhs);
1229    }
1230
1231    private void generateClassLiterals(JsVars vars) {
1232      /*
1233       * Class literals are useless right now, just use String literals and the
1234       * Object methods will basically work.
1235       */

1236      for (Iterator JavaDoc itType = classLits.keySet().iterator(); itType.hasNext();) {
1237        JType type = (JType) itType.next();
1238        JsName jsName = (JsName) classLits.get(type);
1239        String JavaDoc string;
1240        if (type instanceof JArrayType) {
1241          string = "class " + type.getJsniSignatureName().replace('/', '.');
1242        } else if (type instanceof JClassType) {
1243          string = "class " + type.getName();
1244        } else if (type instanceof JInterfaceType) {
1245          string = "interface " + type.getName();
1246        } else {
1247          string = type.getName();
1248        }
1249        JsStringLiteral stringLiteral = jsProgram.getStringLiteral(string);
1250        JsVar var = new JsVar(jsName);
1251        var.setInitExpr(stringLiteral);
1252        vars.add(var);
1253      }
1254    }
1255
1256    private void generateClassSetup(JClassType x, JsStatements globalStmts) {
1257      generateSeedFuncAndPrototype(x, globalStmts);
1258      generateVTables(x, globalStmts);
1259
1260      if (x == program.getTypeJavaLangObject()) {
1261        // special: setup a "toString" alias for java.lang.Object.toString()
1262
generateToStringAlias(x, globalStmts);
1263      }
1264
1265      generateTypeName(x, globalStmts);
1266      generateTypeId(x, globalStmts);
1267    }
1268
1269    private void generateGwtOnLoad(List JavaDoc entryFuncs, JsStatements globalStmts) {
1270      /**
1271       * <pre>
1272       * function gwtOnLoad(errFn, modName, modBase){
1273       * $moduleName = modName;
1274       * $moduleBase = modBase;
1275       * if (errFn) {
1276       * try {
1277       * init();
1278       * } catch(e) {
1279       * errFn(modName);
1280       * }
1281       * } else {
1282       * init();
1283       * }
1284       * }
1285       * </pre>
1286       */

1287      JsFunction gwtOnLoad = new JsFunction(topScope);
1288      globalStmts.add(gwtOnLoad.makeStmt());
1289      JsName gwtOnLoadName = topScope.declareName("gwtOnLoad");
1290      gwtOnLoadName.setObfuscatable(false);
1291      gwtOnLoad.setName(gwtOnLoadName);
1292      JsBlock body = new JsBlock();
1293      gwtOnLoad.setBody(body);
1294      JsScope fnScope = gwtOnLoad.getScope();
1295      JsParameters params = gwtOnLoad.getParameters();
1296      JsName errFn = fnScope.declareName("errFn");
1297      JsName modName = fnScope.declareName("modName");
1298      JsName modBase = fnScope.declareName("modBase");
1299      params.add(new JsParameter(errFn));
1300      params.add(new JsParameter(modName));
1301      params.add(new JsParameter(modBase));
1302      JsExpression asg = createAssignment(
1303          topScope.findExistingUnobfuscatableName("$moduleName").makeRef(),
1304          modName.makeRef());
1305      body.getStatements().add(asg.makeStmt());
1306      asg = createAssignment(topScope.findExistingUnobfuscatableName(
1307          "$moduleBase").makeRef(), modBase.makeRef());
1308      body.getStatements().add(asg.makeStmt());
1309      JsIf jsIf = new JsIf();
1310      body.getStatements().add(jsIf);
1311      jsIf.setIfExpr(errFn.makeRef());
1312      JsTry jsTry = new JsTry();
1313      jsIf.setThenStmt(jsTry);
1314      JsBlock callBlock = new JsBlock();
1315      jsIf.setElseStmt(callBlock);
1316      jsTry.setTryBlock(callBlock);
1317      for (int i = 0; i < entryFuncs.size(); ++i) {
1318        JsFunction func = (JsFunction) entryFuncs.get(i);
1319        if (func != null) {
1320          JsInvocation call = new JsInvocation();
1321          call.setQualifier(func.getName().makeRef());
1322          callBlock.getStatements().add(call.makeStmt());
1323        }
1324      }
1325      JsCatch jsCatch = new JsCatch(fnScope, "e");
1326      jsTry.getCatches().add(jsCatch);
1327      JsBlock catchBlock = new JsBlock();
1328      jsCatch.setBody(catchBlock);
1329      JsInvocation errCall = new JsInvocation();
1330      catchBlock.getStatements().add(errCall.makeStmt());
1331      errCall.setQualifier(errFn.makeRef());
1332      errCall.getArguments().add(modName.makeRef());
1333    }
1334
1335    private void generateNullFunc(JsStatements globalStatements) {
1336      // handle null method
1337
JsFunction nullFunc = new JsFunction(topScope, nullMethodName);
1338      nullFunc.setBody(new JsBlock());
1339      globalStatements.add(nullFunc.makeStmt());
1340    }
1341
1342    private void generatePackageNames(JsVars vars) {
1343      for (Iterator JavaDoc it = packageNames.entrySet().iterator(); it.hasNext();) {
1344        Map.Entry JavaDoc entry = (Entry) it.next();
1345        String JavaDoc packageName = (String JavaDoc) entry.getKey();
1346        JsName name = (JsName) entry.getValue();
1347        JsVar jsVar = new JsVar(name);
1348        jsVar.setInitExpr(jsProgram.getStringLiteral(packageName));
1349        vars.add(jsVar);
1350      }
1351    }
1352
1353    private void generateSeedFuncAndPrototype(JClassType x,
1354        JsStatements globalStmts) {
1355      if (x != program.getTypeJavaLangString()) {
1356        JsName seedFuncName = getName(x);
1357
1358        // seed function
1359
// function com_example_foo_Foo() { }
1360
JsFunction seedFunc = new JsFunction(topScope, seedFuncName);
1361        JsBlock body = new JsBlock();
1362        seedFunc.setBody(body);
1363        globalStmts.add(seedFunc.makeStmt());
1364
1365        // setup prototype, assign to temp
1366
// _ = com_example_foo_Foo.prototype = new com_example_foo_FooSuper();
1367
JsNameRef lhs = prototype.makeRef();
1368        lhs.setQualifier(seedFuncName.makeRef());
1369        JsExpression rhs;
1370        if (x.extnds != null) {
1371          JsNew newExpr = new JsNew();
1372          newExpr.setConstructorExpression(getName(x.extnds).makeRef());
1373          rhs = newExpr;
1374        } else {
1375          rhs = new JsObjectLiteral();
1376        }
1377        JsExpression protoAsg = createAssignment(lhs, rhs);
1378        JsExpression tmpAsg = createAssignment(globalTemp.makeRef(), protoAsg);
1379        globalStmts.add(tmpAsg.makeStmt());
1380      } else {
1381        /*
1382         * MAGIC: java.lang.String is implemented as a JavaScript String
1383         * primitive with a modified prototype.
1384         */

1385        JsNameRef rhs = prototype.makeRef();
1386        rhs.setQualifier(jsProgram.getRootScope().declareName("String").makeRef());
1387        JsExpression tmpAsg = createAssignment(globalTemp.makeRef(), rhs);
1388        globalStmts.add(tmpAsg.makeStmt());
1389      }
1390    }
1391
1392    private void generateToStringAlias(JClassType x, JsStatements globalStmts) {
1393      JMethod toStringMeth = program.getSpecialMethod("Object.toString");
1394      if (x.methods.contains(toStringMeth)) {
1395        // _.toString = function(){return this.java_lang_Object_toString();}
1396

1397        // lhs
1398
JsName lhsName = objectScope.declareName("toString");
1399        lhsName.setObfuscatable(false);
1400        JsNameRef lhs = lhsName.makeRef();
1401        lhs.setQualifier(globalTemp.makeRef());
1402
1403        // rhs
1404
JsInvocation call = new JsInvocation();
1405        JsNameRef toStringRef = new JsNameRef(getPolyName(toStringMeth));
1406        toStringRef.setQualifier(new JsThisRef());
1407        call.setQualifier(toStringRef);
1408        JsReturn jsReturn = new JsReturn(call);
1409        JsFunction rhs = new JsFunction(topScope);
1410        JsBlock body = new JsBlock();
1411        body.getStatements().add(jsReturn);
1412        rhs.setBody(body);
1413
1414        // asg
1415
JsExpression asg = createAssignment(lhs, rhs);
1416        globalStmts.add(new JsExprStmt(asg));
1417      }
1418    }
1419
1420    private void generateTypeId(JClassType x, JsStatements globalStmts) {
1421      int typeId = program.getTypeId(x);
1422      if (typeId >= 0) {
1423        JField typeIdField = program.getSpecialField("Object.typeId");
1424        JsNameRef fieldRef = getName(typeIdField).makeRef();
1425        fieldRef.setQualifier(globalTemp.makeRef());
1426        JsIntegralLiteral typeIdLit = jsProgram.getIntegralLiteral(BigInteger.valueOf(typeId));
1427        JsExpression asg = createAssignment(fieldRef, typeIdLit);
1428        globalStmts.add(new JsExprStmt(asg));
1429      }
1430    }
1431
1432    private void generateTypeName(JClassType x, JsStatements globalStmts) {
1433      JField typeIdField = program.getSpecialField("Object.typeName");
1434      JsNameRef lhs = getName(typeIdField).makeRef();
1435      lhs.setQualifier(globalTemp.makeRef());
1436
1437      // Split the full class name into package + class so package strings
1438
// can be merged.
1439
String JavaDoc className = getClassName(x.getName());
1440      String JavaDoc packageName = getPackageName(x.getName());
1441      JsExpression rhs;
1442      if (packageName != null) {
1443        // use "com.example.foo." + "Foo"
1444
JsName name = (JsName) packageNames.get(packageName);
1445        rhs = new JsBinaryOperation(JsBinaryOperator.ADD, name.makeRef(),
1446            jsProgram.getStringLiteral(className));
1447      } else {
1448        // no package name could be split, just use the full name
1449
rhs = jsProgram.getStringLiteral(x.getName());
1450      }
1451      JsExpression asg = createAssignment(lhs, rhs);
1452      globalStmts.add(new JsExprStmt(asg));
1453    }
1454
1455    private void generateTypeTable(JsVars vars) {
1456      JField typeIdArray = program.getSpecialField("Cast.typeIdArray");
1457      JsName jsName = getName(typeIdArray);
1458      JsArrayLiteral arrayLit = new JsArrayLiteral();
1459      for (int i = 0; i < program.getJsonTypeTable().size(); ++i) {
1460        JsonObject jsonObject = (JsonObject) program.getJsonTypeTable().get(i);
1461        accept(jsonObject);
1462        arrayLit.getExpressions().add((JsExpression) pop());
1463      }
1464      JsVar var = new JsVar(jsName);
1465      var.setInitExpr(arrayLit);
1466      vars.add(var);
1467    }
1468
1469    private void generateVTables(JClassType x, JsStatements globalStmts) {
1470      for (int i = 0; i < x.methods.size(); ++i) {
1471        JMethod method = (JMethod) x.methods.get(i);
1472        if (!method.isStatic() && !method.isAbstract()) {
1473          JsNameRef lhs = getPolyName(method).makeRef();
1474          lhs.setQualifier(globalTemp.makeRef());
1475          JsNameRef rhs = getName(method).makeRef();
1476          JsExpression asg = createAssignment(lhs, rhs);
1477          globalStmts.add(new JsExprStmt(asg));
1478        }
1479      }
1480    }
1481
1482    private void handleClinit(JsFunction clinitFunc, JReferenceType chainTo) {
1483      JsStatements statements = clinitFunc.getBody().getStatements();
1484      // self-assign to the null method immediately (to prevent reentrancy)
1485
JsExpression asg = createAssignment(clinitFunc.getName().makeRef(),
1486          nullMethodName.makeRef());
1487      statements.add(0, asg.makeStmt());
1488      if (chainTo != null) {
1489        JMethod chainToMeth = (JMethod) chainTo.methods.get(0);
1490        JsInvocation jsInvocation = new JsInvocation();
1491        JsNameRef qualifier = getName(chainToMeth).makeRef();
1492        jsInvocation.setQualifier(qualifier);
1493        statements.add(1, jsInvocation.makeStmt());
1494      }
1495    }
1496
1497    private JsInvocation maybeCreateClinitCall(JField x) {
1498      if (!x.isStatic()) {
1499        return null;
1500      }
1501
1502      JReferenceType enclosingType = x.getEnclosingType();
1503      if (!typeOracle.checkClinit(currentMethod.getEnclosingType(),
1504          enclosingType)) {
1505        return null;
1506      }
1507
1508      // The actual target class might not have a direct clinit; find the super
1509
// class that does.
1510
while (!typeOracle.hasDirectClinit(enclosingType)) {
1511        enclosingType = enclosingType.extnds;
1512      }
1513      JMethod clinitMethod = (JMethod) enclosingType.methods.get(0);
1514      JsInvocation jsInvocation = new JsInvocation();
1515      jsInvocation.setQualifier(getName(clinitMethod).makeRef());
1516      return jsInvocation;
1517    }
1518
1519    private JsInvocation maybeCreateClinitCall(JMethod x) {
1520      if (!x.isStatic()) {
1521        return null;
1522      }
1523      JReferenceType enclosingType = x.getEnclosingType();
1524      if (!typeOracle.hasClinit(enclosingType)) {
1525        return null;
1526      }
1527      if (program.isStaticImpl(x)) {
1528        return null;
1529      }
1530      // avoid recursion sickness
1531
if (x == enclosingType.methods.get(0)) {
1532        return null;
1533      }
1534
1535      // The actual target class might not have a direct clinit; find the super
1536
// class that does.
1537
while (!typeOracle.hasDirectClinit(enclosingType)) {
1538        enclosingType = enclosingType.extnds;
1539      }
1540      JMethod clinitMethod = (JMethod) enclosingType.methods.get(0);
1541      JsInvocation jsInvocation = new JsInvocation();
1542      jsInvocation.setQualifier(getName(clinitMethod).makeRef());
1543      return jsInvocation;
1544    }
1545
1546    private JsNode pop() {
1547      return (JsNode) nodeStack.pop();
1548    }
1549
1550    private/* <T extends JsNode> */List JavaDoc/* <T> */popList(int count) {
1551      JsNode[] array = new JsNode[count];
1552      while (count > 0) {
1553        array[--count] = pop();
1554      }
1555
1556      List JavaDoc/* <T> */list = new ArrayList JavaDoc/* <T> */();
1557      for (int i = 0; i < array.length; i++) {
1558        JsNode item = array[i];
1559        if (item != null) {
1560          list.add(/* (T) */item);
1561        }
1562      }
1563      return list;
1564    }
1565
1566    private void popList(JsCollection collection, int count) {
1567      JsNode[] array = new JsNode[count];
1568      while (count > 0) {
1569        array[--count] = pop();
1570      }
1571
1572      for (int i = 0; i < array.length; i++) {
1573        JsNode item = array[i];
1574        if (item != null) {
1575          collection.addNode(item);
1576        }
1577      }
1578    }
1579
1580    private void push(JsNode node) {
1581      nodeStack.push(node);
1582    }
1583  }
1584
1585  private static class JavaToJsOperatorMap {
1586    private static final Map JavaDoc/* <JBinaryOperator, JsBinaryOperator> */bOpMap = new IdentityHashMap JavaDoc();
1587    private static final Map JavaDoc/* <JUnaryOperator, JsUnaryOperator> */uOpMap = new IdentityHashMap JavaDoc();
1588
1589    static {
1590      bOpMap.put(JBinaryOperator.MUL, JsBinaryOperator.MUL);
1591      bOpMap.put(JBinaryOperator.DIV, JsBinaryOperator.DIV);
1592      bOpMap.put(JBinaryOperator.MOD, JsBinaryOperator.MOD);
1593      bOpMap.put(JBinaryOperator.ADD, JsBinaryOperator.ADD);
1594      bOpMap.put(JBinaryOperator.SUB, JsBinaryOperator.SUB);
1595      bOpMap.put(JBinaryOperator.SHL, JsBinaryOperator.SHL);
1596      bOpMap.put(JBinaryOperator.SHR, JsBinaryOperator.SHR);
1597      bOpMap.put(JBinaryOperator.SHRU, JsBinaryOperator.SHRU);
1598      bOpMap.put(JBinaryOperator.LT, JsBinaryOperator.LT);
1599      bOpMap.put(JBinaryOperator.LTE, JsBinaryOperator.LTE);
1600      bOpMap.put(JBinaryOperator.GT, JsBinaryOperator.GT);
1601      bOpMap.put(JBinaryOperator.GTE, JsBinaryOperator.GTE);
1602      bOpMap.put(JBinaryOperator.EQ, JsBinaryOperator.EQ);
1603      bOpMap.put(JBinaryOperator.NEQ, JsBinaryOperator.NEQ);
1604      bOpMap.put(JBinaryOperator.BIT_AND, JsBinaryOperator.BIT_AND);
1605      bOpMap.put(JBinaryOperator.BIT_XOR, JsBinaryOperator.BIT_XOR);
1606      bOpMap.put(JBinaryOperator.BIT_OR, JsBinaryOperator.BIT_OR);
1607      bOpMap.put(JBinaryOperator.AND, JsBinaryOperator.AND);
1608      bOpMap.put(JBinaryOperator.OR, JsBinaryOperator.OR);
1609      bOpMap.put(JBinaryOperator.ASG, JsBinaryOperator.ASG);
1610      bOpMap.put(JBinaryOperator.ASG_ADD, JsBinaryOperator.ASG_ADD);
1611      bOpMap.put(JBinaryOperator.ASG_SUB, JsBinaryOperator.ASG_SUB);
1612      bOpMap.put(JBinaryOperator.ASG_MUL, JsBinaryOperator.ASG_MUL);
1613      bOpMap.put(JBinaryOperator.ASG_DIV, JsBinaryOperator.ASG_DIV);
1614      bOpMap.put(JBinaryOperator.ASG_MOD, JsBinaryOperator.ASG_MOD);
1615      bOpMap.put(JBinaryOperator.ASG_SHL, JsBinaryOperator.ASG_SHL);
1616      bOpMap.put(JBinaryOperator.ASG_SHR, JsBinaryOperator.ASG_SHR);
1617      bOpMap.put(JBinaryOperator.ASG_SHRU, JsBinaryOperator.ASG_SHRU);
1618      bOpMap.put(JBinaryOperator.ASG_BIT_AND, JsBinaryOperator.ASG_BIT_AND);
1619      bOpMap.put(JBinaryOperator.ASG_BIT_OR, JsBinaryOperator.ASG_BIT_OR);
1620      bOpMap.put(JBinaryOperator.ASG_BIT_XOR, JsBinaryOperator.ASG_BIT_XOR);
1621
1622      uOpMap.put(JUnaryOperator.INC, JsUnaryOperator.INC);
1623      uOpMap.put(JUnaryOperator.DEC, JsUnaryOperator.DEC);
1624      uOpMap.put(JUnaryOperator.NEG, JsUnaryOperator.NEG);
1625      uOpMap.put(JUnaryOperator.NOT, JsUnaryOperator.NOT);
1626      uOpMap.put(JUnaryOperator.BIT_NOT, JsUnaryOperator.BIT_NOT);
1627    }
1628
1629    public static JsBinaryOperator get(JBinaryOperator op) {
1630      return (JsBinaryOperator) bOpMap.get(op);
1631    }
1632
1633    public static JsUnaryOperator get(JUnaryOperator op) {
1634      return (JsUnaryOperator) uOpMap.get(op);
1635    }
1636  }
1637
1638  private class SortVisitor extends JVisitor {
1639
1640    private final HasNameSort hasNameSort = new HasNameSort();
1641
1642    public void endVisit(JClassType x, Context ctx) {
1643      Collections.sort(x.fields, hasNameSort);
1644
1645      // Sort the methods manually to avoid sorting clinit out of place!
1646
List JavaDoc methods = x.methods;
1647      Object JavaDoc a[] = methods.toArray();
1648      Arrays.sort(a, 1, a.length, hasNameSort);
1649      for (int i = 1; i < a.length; i++) {
1650        methods.set(i, a[i]);
1651      }
1652    }
1653
1654    public void endVisit(JInterfaceType x, Context ctx) {
1655      Collections.sort(x.fields, hasNameSort);
1656      Collections.sort(x.methods, hasNameSort);
1657    }
1658
1659    public void endVisit(JMethod x, Context ctx) {
1660      Collections.sort(x.locals, hasNameSort);
1661    }
1662
1663    public void endVisit(JProgram x, Context ctx) {
1664      Collections.sort(x.entryMethods, hasNameSort);
1665      Collections.sort(x.getDeclaredTypes(), hasNameSort);
1666    }
1667  }
1668
1669  public static void exec(JProgram program, JsProgram jsProgram) {
1670    GenerateJavaScriptAST generateJavaScriptAST = new GenerateJavaScriptAST(
1671        program, jsProgram);
1672    generateJavaScriptAST.execImpl();
1673  }
1674
1675  private static String JavaDoc getClassName(String JavaDoc fullName) {
1676    int pos = fullName.lastIndexOf(".");
1677    return fullName.substring(pos + 1);
1678  }
1679
1680  private static String JavaDoc getPackageName(String JavaDoc fullName) {
1681    int pos = fullName.lastIndexOf(".");
1682    return fullName.substring(0, pos + 1);
1683  }
1684
1685  private final Map JavaDoc/* <JBlock, JsCatch> */catchMap = new IdentityHashMap JavaDoc();
1686  private final Map JavaDoc/* <JType, JsName> */classLits = new IdentityHashMap JavaDoc();
1687  private final Map JavaDoc/* <JClassType, JsScope> */classScopes = new IdentityHashMap JavaDoc();
1688
1689  /**
1690   * Contains JsNames for all interface methods. A special scope is needed so
1691   * that independent classes will obfuscate their interface implementation
1692   * methods the same way.
1693   */

1694  private final JsScope interfaceScope;
1695
1696  private final JsProgram jsProgram;
1697  private final Map JavaDoc/* <JMethod, JsFunction> */methodMap = new IdentityHashMap JavaDoc();
1698  private final Map JavaDoc/* <HasName, JsName> */names = new IdentityHashMap JavaDoc();
1699  private JsName nullMethodName;
1700
1701  /**
1702   * Contains JsNames for the Object instance methods, such as equals, hashCode,
1703   * and toString. All other class scopes have this scope as an ultimate parent.
1704   */

1705  private final JsScope objectScope;
1706  private final Map JavaDoc/* <String, JsName> */packageNames = new TreeMap JavaDoc();
1707  private final Map JavaDoc/* <JMethod, JsName> */polymorphicNames = new IdentityHashMap JavaDoc();
1708  private final JProgram program;
1709
1710  /**
1711   * Contains JsNames for all globals, such as static fields and methods.
1712   */

1713  private final JsScope topScope;
1714
1715  private final JTypeOracle typeOracle;
1716
1717  private GenerateJavaScriptAST(JProgram program, JsProgram jsProgram) {
1718    this.program = program;
1719    typeOracle = program.typeOracle;
1720    this.jsProgram = jsProgram;
1721    topScope = jsProgram.getScope();
1722    objectScope = jsProgram.getObjectScope();
1723    interfaceScope = new JsScope(objectScope, "Interfaces");
1724  }
1725
1726  String JavaDoc getNameString(HasName hasName) {
1727    String JavaDoc s = hasName.getName().replaceAll("_", "_1").replace('.', '_');
1728    return s;
1729  }
1730
1731  String JavaDoc mangleName(JField x) {
1732    String JavaDoc s = getNameString(x.getEnclosingType()) + '_' + getNameString(x);
1733    return s;
1734  }
1735
1736  String JavaDoc mangleNameForGlobal(JMethod x) {
1737    String JavaDoc s = getNameString(x.getEnclosingType()) + '_' + getNameString(x)
1738        + "__";
1739    for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) {
1740      JType type = (JType) x.getOriginalParamTypes().get(i);
1741      s += type.getJavahSignatureName();
1742    }
1743    return s;
1744  }
1745
1746  String JavaDoc mangleNameForPoly(JMethod x) {
1747    String JavaDoc s = getNameString(x) + "__";
1748    for (int i = 0; i < x.getOriginalParamTypes().size(); ++i) {
1749      JType type = (JType) x.getOriginalParamTypes().get(i);
1750      s += type.getJavahSignatureName();
1751    }
1752    return s;
1753  }
1754
1755  private void execImpl() {
1756    SortVisitor sorter = new SortVisitor();
1757    sorter.accept(program);
1758    CreateNamesAndScopesVisitor creator = new CreateNamesAndScopesVisitor();
1759    creator.accept(program);
1760    GenerateJavaScriptVisitor generator = new GenerateJavaScriptVisitor();
1761    generator.accept(program);
1762  }
1763
1764  private JsName getName(HasName x) {
1765    return (JsName) names.get(x);
1766  }
1767
1768  private JsName getPolyName(HasName x) {
1769    return (JsName) polymorphicNames.get(x);
1770  }
1771
1772}
1773
Popular Tags