KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > dev > js > JsParser


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.js;
17
18 import com.google.gwt.dev.js.ast.JsArrayAccess;
19 import com.google.gwt.dev.js.ast.JsArrayLiteral;
20 import com.google.gwt.dev.js.ast.JsBinaryOperation;
21 import com.google.gwt.dev.js.ast.JsBinaryOperator;
22 import com.google.gwt.dev.js.ast.JsBlock;
23 import com.google.gwt.dev.js.ast.JsBooleanLiteral;
24 import com.google.gwt.dev.js.ast.JsBreak;
25 import com.google.gwt.dev.js.ast.JsCase;
26 import com.google.gwt.dev.js.ast.JsCatch;
27 import com.google.gwt.dev.js.ast.JsConditional;
28 import com.google.gwt.dev.js.ast.JsContinue;
29 import com.google.gwt.dev.js.ast.JsDefault;
30 import com.google.gwt.dev.js.ast.JsDoWhile;
31 import com.google.gwt.dev.js.ast.JsExpression;
32 import com.google.gwt.dev.js.ast.JsExpressions;
33 import com.google.gwt.dev.js.ast.JsFor;
34 import com.google.gwt.dev.js.ast.JsForIn;
35 import com.google.gwt.dev.js.ast.JsFunction;
36 import com.google.gwt.dev.js.ast.JsIf;
37 import com.google.gwt.dev.js.ast.JsInvocation;
38 import com.google.gwt.dev.js.ast.JsLabel;
39 import com.google.gwt.dev.js.ast.JsName;
40 import com.google.gwt.dev.js.ast.JsNameRef;
41 import com.google.gwt.dev.js.ast.JsNew;
42 import com.google.gwt.dev.js.ast.JsNode;
43 import com.google.gwt.dev.js.ast.JsObjectLiteral;
44 import com.google.gwt.dev.js.ast.JsParameter;
45 import com.google.gwt.dev.js.ast.JsPostfixOperation;
46 import com.google.gwt.dev.js.ast.JsPrefixOperation;
47 import com.google.gwt.dev.js.ast.JsProgram;
48 import com.google.gwt.dev.js.ast.JsPropertyInitializer;
49 import com.google.gwt.dev.js.ast.JsRegExp;
50 import com.google.gwt.dev.js.ast.JsReturn;
51 import com.google.gwt.dev.js.ast.JsScope;
52 import com.google.gwt.dev.js.ast.JsStatement;
53 import com.google.gwt.dev.js.ast.JsStatements;
54 import com.google.gwt.dev.js.ast.JsStringLiteral;
55 import com.google.gwt.dev.js.ast.JsSwitch;
56 import com.google.gwt.dev.js.ast.JsThisRef;
57 import com.google.gwt.dev.js.ast.JsThrow;
58 import com.google.gwt.dev.js.ast.JsTry;
59 import com.google.gwt.dev.js.ast.JsUnaryOperator;
60 import com.google.gwt.dev.js.ast.JsVars;
61 import com.google.gwt.dev.js.ast.JsWhile;
62 import com.google.gwt.dev.js.rhino.Context;
63 import com.google.gwt.dev.js.rhino.ErrorReporter;
64 import com.google.gwt.dev.js.rhino.EvaluatorException;
65 import com.google.gwt.dev.js.rhino.IRFactory;
66 import com.google.gwt.dev.js.rhino.Node;
67 import com.google.gwt.dev.js.rhino.Parser;
68 import com.google.gwt.dev.js.rhino.TokenStream;
69
70 import java.io.IOException JavaDoc;
71 import java.io.Reader JavaDoc;
72 import java.math.BigInteger JavaDoc;
73 import java.util.Iterator JavaDoc;
74 import java.util.Stack JavaDoc;
75
76 /**
77  * Parses JavaScript source.
78  */

79 public class JsParser {
80
81   private JsProgram program;
82   private final Stack JavaDoc scopeStack = new Stack JavaDoc();
83
84   public JsParser() {
85     // Create a custom error handler so that we can throw our own exceptions.
86
//
87
Context.enter().setErrorReporter(new ErrorReporter() {
88       public void error(String JavaDoc msg, String JavaDoc loc, int ln, String JavaDoc src, int col) {
89         throw new UncheckedJsParserException(new JsParserException(msg, ln,
90             src, col));
91       }
92
93       public EvaluatorException runtimeError(String JavaDoc msg, String JavaDoc loc, int ln,
94           String JavaDoc src, int col) {
95         // Never called, but just in case.
96
throw new UncheckedJsParserException(new JsParserException(msg, ln,
97             src, col));
98       }
99
100       public void warning(String JavaDoc msg, String JavaDoc loc, int ln, String JavaDoc src, int col) {
101         // Ignore warnings.
102
}
103     });
104   }
105
106   public JsStatements parse(JsScope scope, Reader JavaDoc r, int startLine)
107       throws IOException JavaDoc, JsParserException {
108     try {
109       // Parse using the Rhino parser.
110
//
111
TokenStream ts = new TokenStream(r, "", startLine);
112       Parser parser = new Parser(new IRFactory(ts));
113       Node topNode = (Node) parser.parse(ts);
114
115       // Map the Rhino AST to ours.
116
//
117
program = scope.getProgram();
118       pushScope(scope);
119       JsStatements stmts = mapStatements(topNode);
120       popScope();
121
122       return stmts;
123     } catch (UncheckedJsParserException e) {
124       throw e.getParserException();
125     }
126   }
127
128   public void parseInto(JsScope scope, JsBlock block, Reader JavaDoc r, int startLine)
129       throws IOException JavaDoc, JsParserException {
130     JsStatements childStmts = parse(scope, r, startLine);
131     JsStatements parentStmts = block.getStatements();
132     for (Iterator JavaDoc iter = childStmts.iterator(); iter.hasNext();) {
133       parentStmts.add((JsStatement) iter.next());
134     }
135   }
136
137   private JsParserException createParserException(String JavaDoc msg, Node offender) {
138     return new JsParserException(msg);
139   }
140
141   private JsScope getScope() {
142     return (JsScope) scopeStack.peek();
143   }
144
145   private JsNode map(Node node) throws JsParserException {
146
147     switch (node.getType()) {
148       case TokenStream.SCRIPT:
149       {
150         JsBlock block = new JsBlock();
151         mapStatements(block.getStatements(), node);
152         return block;
153       }
154
155       case TokenStream.DEBUGGER:
156         return mapDebuggerStatement();
157
158       case TokenStream.VOID:
159         // VOID = nothing was parsed for this node
160
return null;
161
162       case TokenStream.EXPRSTMT:
163         return mapExpression(node.getFirstChild()).makeStmt();
164
165       case TokenStream.REGEXP:
166         return mapRegExp(node);
167
168       case TokenStream.ADD:
169         return mapBinaryOperation(JsBinaryOperator.ADD, node);
170
171       case TokenStream.SUB:
172         return mapBinaryOperation(JsBinaryOperator.SUB, node);
173
174       case TokenStream.MUL:
175         return mapBinaryOperation(JsBinaryOperator.MUL, node);
176
177       case TokenStream.DIV:
178         return mapBinaryOperation(JsBinaryOperator.DIV, node);
179
180       case TokenStream.MOD:
181         return mapBinaryOperation(JsBinaryOperator.MOD, node);
182
183       case TokenStream.AND:
184         return mapBinaryOperation(JsBinaryOperator.AND, node);
185
186       case TokenStream.OR:
187         return mapBinaryOperation(JsBinaryOperator.OR, node);
188
189       case TokenStream.BITAND:
190         return mapBinaryOperation(JsBinaryOperator.BIT_AND, node);
191
192       case TokenStream.BITOR:
193         return mapBinaryOperation(JsBinaryOperator.BIT_OR, node);
194
195       case TokenStream.BITXOR:
196         return mapBinaryOperation(JsBinaryOperator.BIT_XOR, node);
197
198       case TokenStream.ASSIGN:
199         return mapAssignmentVariant(node);
200
201       case TokenStream.RELOP:
202         return mapRelationalVariant(node);
203
204       case TokenStream.EQOP:
205         return mapEqualityVariant(node);
206
207       case TokenStream.SHOP:
208         return mapShiftVariant(node);
209
210       case TokenStream.UNARYOP:
211         return mapUnaryVariant(node);
212
213       case TokenStream.INC:
214         return mapIncDecFixity(JsUnaryOperator.INC, node);
215
216       case TokenStream.DEC:
217         return mapIncDecFixity(JsUnaryOperator.DEC, node);
218
219       case TokenStream.HOOK:
220         return mapConditional(node);
221
222       case TokenStream.NAME:
223         return mapName(node);
224
225       case TokenStream.STRING:
226         return program.getStringLiteral(node.getString());
227
228       case TokenStream.NUMBER:
229         return mapNumber(node);
230
231       case TokenStream.CALL:
232         return mapCall(node);
233
234       case TokenStream.GETPROP:
235         return mapGetProp(node);
236
237       case TokenStream.SETPROP:
238         return mapSetProp(node);
239
240       case TokenStream.DELPROP:
241         return mapDeleteProp(node);
242
243       case TokenStream.IF:
244         return mapIfStatement(node);
245
246       case TokenStream.WHILE:
247         return mapDoOrWhileStatement(true, node);
248
249       case TokenStream.DO:
250         return mapDoOrWhileStatement(false, node);
251
252       case TokenStream.FOR:
253         return mapForStatement(node);
254
255       case TokenStream.WITH:
256         return mapWithStatement(node);
257
258       case TokenStream.GETELEM:
259         return mapGetElem(node);
260
261       case TokenStream.SETELEM:
262         return mapSetElem(node);
263
264       case TokenStream.FUNCTION:
265         return mapFunction(node);
266
267       case TokenStream.BLOCK:
268         return mapBlock(node);
269
270       case TokenStream.SETNAME:
271         return mapBinaryOperation(JsBinaryOperator.ASG, node);
272
273       case TokenStream.BINDNAME:
274         return mapName(node);
275
276       case TokenStream.RETURN:
277         return mapReturn(node);
278
279       case TokenStream.BREAK:
280         return mapBreak(node);
281
282       case TokenStream.CONTINUE:
283         return mapContinue(node);
284
285       case TokenStream.OBJLIT:
286         return mapObjectLit(node);
287
288       case TokenStream.ARRAYLIT:
289         return mapArrayLit(node);
290
291       case TokenStream.VAR:
292         return mapVar(node);
293
294       case TokenStream.PRIMARY:
295         return mapPrimary(node);
296
297       case TokenStream.COMMA:
298         return mapBinaryOperation(JsBinaryOperator.COMMA, node);
299
300       case TokenStream.NEW:
301         return mapNew(node);
302
303       case TokenStream.THROW:
304         return mapThrowStatement(node);
305
306       case TokenStream.TRY:
307         return mapTryStatement(node);
308
309       case TokenStream.SWITCH:
310         return mapSwitchStatement(node);
311
312       case TokenStream.LABEL:
313         return mapLabel(node);
314
315       default:
316         int tokenType = node.getType();
317         throw new JsParserException("Unexpected top-level token type: "
318             + tokenType);
319     }
320   }
321
322   private JsArrayLiteral mapArrayLit(Node node) throws JsParserException {
323     JsArrayLiteral toLit = new JsArrayLiteral();
324     Node from = node.getFirstChild();
325     while (from != null) {
326       toLit.getExpressions().add(mapExpression(from));
327       from = from.getNext();
328     }
329     return toLit;
330   }
331
332   /**
333    * Produces a {@link JsNameRef}.
334    */

335   private JsNameRef mapAsPropertyNameRef(Node nameRefNode)
336       throws JsParserException {
337     JsNode unknown = map(nameRefNode);
338     // This is weird, but for "a.b", the rhino AST calls "b" a string literal.
339
// However, since we know it's for a PROPGET, we can unstringliteralize it.
340
//
341
if (unknown instanceof JsStringLiteral) {
342       JsStringLiteral lit = (JsStringLiteral) unknown;
343       String JavaDoc litName = lit.getValue();
344       return new JsNameRef(litName);
345     } else {
346       throw createParserException("Expecting a name reference", nameRefNode);
347     }
348   }
349
350   private JsExpression mapAssignmentVariant(Node asgNode)
351       throws JsParserException {
352     switch (asgNode.getIntDatum()) {
353       case TokenStream.NOP:
354         return mapBinaryOperation(JsBinaryOperator.ASG, asgNode);
355
356       case TokenStream.ADD:
357         return mapBinaryOperation(JsBinaryOperator.ASG_ADD, asgNode);
358
359       case TokenStream.SUB:
360         return mapBinaryOperation(JsBinaryOperator.ASG_SUB, asgNode);
361
362       case TokenStream.MUL:
363         return mapBinaryOperation(JsBinaryOperator.ASG_MUL, asgNode);
364
365       case TokenStream.DIV:
366         return mapBinaryOperation(JsBinaryOperator.ASG_DIV, asgNode);
367
368       case TokenStream.MOD:
369         return mapBinaryOperation(JsBinaryOperator.ASG_MOD, asgNode);
370
371       case TokenStream.BITAND:
372         return mapBinaryOperation(JsBinaryOperator.ASG_BIT_AND, asgNode);
373
374       case TokenStream.BITOR:
375         return mapBinaryOperation(JsBinaryOperator.ASG_BIT_OR, asgNode);
376
377       case TokenStream.BITXOR:
378         return mapBinaryOperation(JsBinaryOperator.ASG_BIT_XOR, asgNode);
379
380       case TokenStream.LSH:
381         return mapBinaryOperation(JsBinaryOperator.ASG_SHL, asgNode);
382
383       case TokenStream.RSH:
384         return mapBinaryOperation(JsBinaryOperator.ASG_SHR, asgNode);
385
386       case TokenStream.URSH:
387         return mapBinaryOperation(JsBinaryOperator.ASG_SHRU, asgNode);
388
389       default:
390         throw new JsParserException("Unknown assignment operator variant: "
391             + asgNode.getIntDatum());
392     }
393   }
394
395   private JsExpression mapBinaryOperation(JsBinaryOperator op, Node node)
396       throws JsParserException {
397     Node from1 = node.getFirstChild();
398     Node from2 = from1.getNext();
399
400     JsExpression to1 = mapExpression(from1);
401     JsExpression to2 = mapExpression(from2);
402
403     return new JsBinaryOperation(op, to1, to2);
404   }
405
406   private JsBlock mapBlock(Node nodeStmts) throws JsParserException {
407     JsBlock block = new JsBlock();
408     mapStatements(block.getStatements(), nodeStmts);
409     return block;
410   }
411
412   private JsBreak mapBreak(Node breakNode) {
413     Node fromLabel = breakNode.getFirstChild();
414     if (fromLabel != null) {
415       return new JsBreak(mapName(fromLabel));
416     } else {
417       return new JsBreak();
418     }
419   }
420
421   private JsInvocation mapCall(Node callNode) throws JsParserException {
422     JsInvocation invocation = new JsInvocation();
423
424     // Map the target expression.
425
//
426
Node from = callNode.getFirstChild();
427     JsExpression to = mapExpression(from);
428     invocation.setQualifier(to);
429
430     // Iterate over and map the arguments.
431
//
432
JsExpressions args = invocation.getArguments();
433     from = from.getNext();
434     while (from != null) {
435       to = mapExpression(from);
436       args.add(to);
437       from = from.getNext();
438     }
439
440     return invocation;
441   }
442
443   private JsExpression mapConditional(Node condNode) throws JsParserException {
444     JsConditional toCond = new JsConditional();
445
446     Node fromTest = condNode.getFirstChild();
447     toCond.setTestExpression(mapExpression(fromTest));
448
449     Node fromThen = fromTest.getNext();
450     toCond.setThenExpression(mapExpression(fromThen));
451
452     Node fromElse = fromThen.getNext();
453     toCond.setElseExpression(mapExpression(fromElse));
454
455     return toCond;
456   }
457
458   private JsContinue mapContinue(Node contNode) {
459     Node fromLabel = contNode.getFirstChild();
460     if (fromLabel != null) {
461       return new JsContinue(mapName(fromLabel));
462     } else {
463       return new JsContinue();
464     }
465   }
466
467   private JsStatement mapDebuggerStatement() {
468     // Calls an optional method to invoke the debugger.
469
//
470
return program.getDebuggerStmt();
471   }
472
473   private JsNode mapDeleteProp(Node node) throws JsParserException {
474     Node from = node.getFirstChild();
475     JsExpression to = mapExpression(from);
476     if (to instanceof JsNameRef) {
477       return new JsPrefixOperation(JsUnaryOperator.DELETE, to);
478     } else if (to instanceof JsArrayAccess) {
479       return new JsPrefixOperation(JsUnaryOperator.DELETE, to);
480     } else {
481       throw createParserException(
482           "'delete' can only operate on property names and array elements",
483           from);
484     }
485   }
486
487   private JsStatement mapDoOrWhileStatement(boolean isWhile, Node ifNode)
488       throws JsParserException {
489
490     // Pull out the pieces we want to map.
491
//
492
Node fromTestExpr;
493     Node fromBody;
494     if (isWhile) {
495       fromTestExpr = ifNode.getFirstChild();
496       fromBody = ifNode.getFirstChild().getNext();
497     } else {
498       fromBody = ifNode.getFirstChild();
499       fromTestExpr = ifNode.getFirstChild().getNext();
500     }
501
502     // Map the test expression.
503
//
504
JsExpression toTestExpr = mapExpression(fromTestExpr);
505
506     // Map the body block.
507
//
508
JsStatement toBody = mapStatement(fromBody);
509
510     // Create and attach the "while" or "do" statement we're mapping to.
511
//
512
if (isWhile) {
513       return new JsWhile(toTestExpr, toBody);
514     } else {
515       return new JsDoWhile(toTestExpr, toBody);
516     }
517   }
518
519   private JsExpression mapEqualityVariant(Node eqNode) throws JsParserException {
520     switch (eqNode.getIntDatum()) {
521       case TokenStream.EQ:
522         return mapBinaryOperation(JsBinaryOperator.EQ, eqNode);
523
524       case TokenStream.NE:
525         return mapBinaryOperation(JsBinaryOperator.NEQ, eqNode);
526
527       case TokenStream.SHEQ:
528         return mapBinaryOperation(JsBinaryOperator.REF_EQ, eqNode);
529
530       case TokenStream.SHNE:
531         return mapBinaryOperation(JsBinaryOperator.REF_NEQ, eqNode);
532
533       case TokenStream.LT:
534         return mapBinaryOperation(JsBinaryOperator.LT, eqNode);
535
536       case TokenStream.LE:
537         return mapBinaryOperation(JsBinaryOperator.LTE, eqNode);
538
539       case TokenStream.GT:
540         return mapBinaryOperation(JsBinaryOperator.GT, eqNode);
541
542       case TokenStream.GE:
543         return mapBinaryOperation(JsBinaryOperator.GTE, eqNode);
544
545       default:
546         throw new JsParserException("Unknown equality operator variant: "
547             + eqNode.getIntDatum());
548     }
549   }
550
551   private JsExpression mapExpression(Node exprNode) throws JsParserException {
552     JsNode unknown = map(exprNode);
553     if (unknown instanceof JsExpression) {
554       return (JsExpression) unknown;
555     } else {
556       throw createParserException("Expecting an expression", exprNode);
557     }
558   }
559
560   private JsStatement mapForStatement(Node forNode) throws JsParserException {
561     Node fromInit = forNode.getFirstChild();
562     Node fromTest = fromInit.getNext();
563     Node fromIncr = fromTest.getNext();
564     Node fromBody = fromIncr.getNext();
565
566     if (fromBody == null) {
567       // This could be a "for...in" structure.
568
// We could based on the different child layout.
569
//
570
Node fromIter = forNode.getFirstChild();
571       Node fromObjExpr = fromIter.getNext();
572       fromBody = fromObjExpr.getNext();
573
574       JsForIn toForIn;
575       if (fromIter.getType() == TokenStream.VAR) {
576         // A named iterator var.
577
//
578
Node fromIterVarName = fromIter.getFirstChild();
579         String JavaDoc fromName = fromIterVarName.getString();
580         JsName toName = getScope().declareName(fromName);
581         toForIn = new JsForIn(toName);
582         Node fromIterInit = fromIterVarName.getFirstChild();
583         if (fromIterInit != null) {
584           // That has an initializer expression (useful only for side effects).
585
//
586
toForIn.setIterExpr(mapOptionalExpression(fromIterInit));
587         }
588       } else {
589         // An unnamed iterator var.
590
//
591
toForIn = new JsForIn();
592         toForIn.setIterExpr(mapExpression(fromIter));
593       }
594       toForIn.setObjExpr(mapExpression(fromObjExpr));
595
596       // The body stmt.
597
//
598
JsStatement bodyStmt = mapStatement(fromBody);
599       if (bodyStmt != null) {
600         toForIn.setBody(bodyStmt);
601       } else {
602         toForIn.setBody(program.getEmptyStmt());
603       }
604
605       return toForIn;
606     } else {
607       // Regular ol' for loop.
608
//
609
JsFor toFor = new JsFor();
610
611       // The first item is either an expression or a JsVars.
612
//
613
JsNode initThingy = map(fromInit);
614       if (initThingy != null) {
615         if (initThingy instanceof JsVars) {
616           toFor.setInitVars((JsVars) initThingy);
617         } else {
618           assert (initThingy instanceof JsExpression);
619           toFor.setInitExpr((JsExpression) initThingy);
620         }
621       }
622       toFor.setCondition(mapOptionalExpression(fromTest));
623       toFor.setIncrExpr(mapOptionalExpression(fromIncr));
624
625       JsStatement bodyStmt = mapStatement(fromBody);
626       if (bodyStmt != null) {
627         toFor.setBody(bodyStmt);
628       } else {
629         toFor.setBody(program.getEmptyStmt());
630       }
631       return toFor;
632     }
633   }
634
635   private JsExpression mapFunction(Node fnNode) throws JsParserException {
636
637     Node fromFnNameNode = fnNode.getFirstChild();
638     Node fromParamNode = fnNode.getFirstChild().getNext().getFirstChild();
639     Node fromBodyNode = fnNode.getFirstChild().getNext().getNext();
640
641     // Decide the function's name, if any.
642
//
643
String JavaDoc fromFnName = fromFnNameNode.getString();
644     JsName toFnName = null;
645     if (fromFnName != null && fromFnName.length() > 0) {
646       toFnName = getScope().declareName(fromFnName);
647     }
648
649     // Create it, and set the params.
650
//
651
JsFunction toFn = new JsFunction(getScope(), toFnName);
652
653     // Creating a function also creates a new scope, which we push onto
654
// the scope stack.
655
//
656
pushScope(toFn.getScope());
657
658     while (fromParamNode != null) {
659       String JavaDoc fromParamName = fromParamNode.getString();
660       // should this be unique? I think not since you can have dup args.
661
JsName paramName = toFn.getScope().declareName(fromParamName);
662       toFn.getParameters().add(new JsParameter(paramName));
663       fromParamNode = fromParamNode.getNext();
664     }
665
666     // Map the function's body.
667
//
668
JsBlock toBody = mapBlock(fromBodyNode);
669     toFn.setBody(toBody);
670
671     // Pop the new function's scope off of the scope stack.
672
//
673
popScope();
674
675     return toFn;
676   }
677
678   private JsArrayAccess mapGetElem(Node getElemNode) throws JsParserException {
679     Node from1 = getElemNode.getFirstChild();
680     Node from2 = from1.getNext();
681
682     JsExpression to1 = mapExpression(from1);
683     JsExpression to2 = mapExpression(from2);
684
685     return new JsArrayAccess(to1, to2);
686   }
687
688   private JsNameRef mapGetProp(Node getPropNode) throws JsParserException {
689     Node from1 = getPropNode.getFirstChild();
690     Node from2 = from1.getNext();
691
692     JsExpression toQualifier = mapExpression(from1);
693     JsNameRef toNameRef;
694     if (from2 != null) {
695       toNameRef = mapAsPropertyNameRef(from2);
696     } else {
697       // Special properties don't have a second expression.
698
//
699
Object JavaDoc obj = getPropNode.getProp(Node.SPECIAL_PROP_PROP);
700       assert (obj instanceof String JavaDoc);
701       toNameRef = new JsNameRef((String JavaDoc) obj);
702     }
703     toNameRef.setQualifier(toQualifier);
704
705     return toNameRef;
706   }
707
708   private JsIf mapIfStatement(Node ifNode) throws JsParserException {
709
710     // Pull out the pieces we want to map.
711
//
712
Node fromTestExpr = ifNode.getFirstChild();
713     Node fromThenBlock = ifNode.getFirstChild().getNext();
714     Node fromElseBlock = ifNode.getFirstChild().getNext().getNext();
715
716     // Create the "if" statement we're mapping to.
717
//
718
JsIf toIf = new JsIf();
719
720     // Map the test expression.
721
//
722
JsExpression toTestExpr = mapExpression(fromTestExpr);
723     toIf.setIfExpr(toTestExpr);
724
725     // Map the "then" block.
726
//
727
toIf.setThenStmt(mapStatement(fromThenBlock));
728
729     // Map the "else" block.
730
//
731
if (fromElseBlock != null) {
732       toIf.setElseStmt(mapStatement(fromElseBlock));
733     }
734
735     return toIf;
736   }
737
738   private JsExpression mapIncDecFixity(JsUnaryOperator op, Node node)
739       throws JsParserException {
740     switch (node.getIntDatum()) {
741       case TokenStream.PRE:
742         return mapPrefixOperation(op, node);
743       case TokenStream.POST:
744         return mapPostfixOperation(op, node);
745       default:
746         throw new JsParserException("Unknown prefix/postfix variant: "
747             + node.getIntDatum());
748     }
749   }
750
751   private JsLabel mapLabel(Node labelNode) throws JsParserException {
752     String JavaDoc fromName = labelNode.getFirstChild().getString();
753     JsName toName = getScope().declareName(fromName);
754     Node fromStmt = labelNode.getFirstChild().getNext();
755     JsLabel toLabel = new JsLabel(toName);
756     toLabel.setStmt(mapStatement(fromStmt));
757     return toLabel;
758   }
759
760   /**
761    * Creates a reference to a name that may or may not be obfuscatable, based on
762    * whether it matches a known name in the scope.
763    */

764   private JsNameRef mapName(Node node) {
765     String JavaDoc ident = node.getString();
766     return new JsNameRef(ident);
767   }
768
769   private JsNew mapNew(Node newNode) throws JsParserException {
770
771     JsNew newExpr = new JsNew();
772
773     // Map the constructor expression, which is often just the name of
774
// some lambda.
775
//
776
Node fromCtorExpr = newNode.getFirstChild();
777     newExpr.setConstructorExpression(mapExpression(fromCtorExpr));
778
779     // Iterate over and map the arguments.
780
//
781
JsExpressions args = newExpr.getArguments();
782     Node fromArg = fromCtorExpr.getNext();
783     while (fromArg != null) {
784       args.add(mapExpression(fromArg));
785       fromArg = fromArg.getNext();
786     }
787
788     return newExpr;
789   }
790
791   private JsExpression mapNumber(Node numberNode) {
792     double x = numberNode.getDouble();
793     long j = (long) x;
794     if (x == j) {
795       return program.getIntegralLiteral(BigInteger.valueOf(j));
796     } else {
797       return program.getDecimalLiteral(String.valueOf(x));
798     }
799   }
800
801   private JsExpression mapObjectLit(Node objLitNode) throws JsParserException {
802     JsObjectLiteral toLit = new JsObjectLiteral();
803     Node fromPropInit = objLitNode.getFirstChild();
804     while (fromPropInit != null) {
805
806       Node fromLabelExpr = fromPropInit;
807       JsExpression toLabelExpr = mapExpression(fromLabelExpr);
808
809       // Advance to the initializer expression.
810
//
811
fromPropInit = fromPropInit.getNext();
812       Node fromValueExpr = fromPropInit;
813       if (fromValueExpr == null) {
814         throw createParserException("Expected an init expression for: "
815             + toLabelExpr, objLitNode);
816       }
817       JsExpression toValueExpr = mapExpression(fromValueExpr);
818
819       JsPropertyInitializer toPropInit = new JsPropertyInitializer(toLabelExpr,
820           toValueExpr);
821       toLit.getPropertyInitializers().add(toPropInit);
822
823       // Begin the next property initializer, if there is one.
824
//
825
fromPropInit = fromPropInit.getNext();
826     }
827
828     return toLit;
829   }
830
831   private JsExpression mapOptionalExpression(Node exprNode)
832       throws JsParserException {
833     JsNode unknown = map(exprNode);
834     if (unknown != null) {
835       if (unknown instanceof JsExpression) {
836         return (JsExpression) unknown;
837       } else {
838         throw createParserException("Expecting an expression or null", exprNode);
839       }
840     }
841     return null;
842   }
843
844   private JsExpression mapPostfixOperation(JsUnaryOperator op, Node node)
845       throws JsParserException {
846     Node from = node.getFirstChild();
847     JsExpression to = mapExpression(from);
848     return new JsPostfixOperation(op, to);
849   }
850
851   private JsExpression mapPrefixOperation(JsUnaryOperator op, Node node)
852       throws JsParserException {
853     Node from = node.getFirstChild();
854     JsExpression to = mapExpression(from);
855     return new JsPrefixOperation(op, to);
856   }
857
858   private JsExpression mapPrimary(Node node) throws JsParserException {
859     switch (node.getIntDatum()) {
860       case TokenStream.THIS:
861         return new JsThisRef();
862
863       case TokenStream.TRUE:
864         return program.getTrueLiteral();
865
866       case TokenStream.FALSE:
867         return program.getFalseLiteral();
868
869       case TokenStream.NULL:
870         return program.getNullLiteral();
871
872       default:
873         throw new JsParserException("Unknown primary: " + node.getIntDatum());
874     }
875   }
876
877   private JsNode mapRegExp(Node regExpNode) {
878     JsRegExp toRegExp = new JsRegExp();
879
880     Node fromPattern = regExpNode.getFirstChild();
881     toRegExp.setPattern(fromPattern.getString());
882
883     Node fromFlags = fromPattern.getNext();
884     if (fromFlags != null) {
885       toRegExp.setFlags(fromFlags.getString());
886     }
887
888     return toRegExp;
889   }
890
891   private JsExpression mapRelationalVariant(Node relNode)
892       throws JsParserException {
893     switch (relNode.getIntDatum()) {
894       case TokenStream.LT:
895         return mapBinaryOperation(JsBinaryOperator.LT, relNode);
896
897       case TokenStream.LE:
898         return mapBinaryOperation(JsBinaryOperator.LTE, relNode);
899
900       case TokenStream.GT:
901         return mapBinaryOperation(JsBinaryOperator.GT, relNode);
902
903       case TokenStream.GE:
904         return mapBinaryOperation(JsBinaryOperator.GTE, relNode);
905
906       case TokenStream.INSTANCEOF:
907         return mapBinaryOperation(JsBinaryOperator.INSTANCEOF, relNode);
908
909       case TokenStream.IN:
910         return mapBinaryOperation(JsBinaryOperator.INOP, relNode);
911
912       default:
913         throw new JsParserException("Unknown relational operator variant: "
914             + relNode.getIntDatum());
915     }
916   }
917
918   private JsReturn mapReturn(Node returnNode) throws JsParserException {
919     JsReturn toReturn = new JsReturn();
920     Node from = returnNode.getFirstChild();
921     if (from != null) {
922       JsExpression to = mapExpression(from);
923       toReturn.setExpr(to);
924     }
925
926     return toReturn;
927   }
928
929   private JsExpression mapSetElem(Node setElemNode) throws JsParserException {
930     // Reuse the get elem code.
931
//
932
JsArrayAccess lhs = mapGetElem(setElemNode);
933
934     // Map the RHS.
935
//
936
Node fromRhs = setElemNode.getFirstChild().getNext().getNext();
937     JsExpression toRhs = mapExpression(fromRhs);
938
939     return new JsBinaryOperation(JsBinaryOperator.ASG, lhs, toRhs);
940   }
941
942   private JsExpression mapSetProp(Node getPropNode) throws JsParserException {
943     // Reuse the get prop code.
944
//
945
JsNameRef lhs = mapGetProp(getPropNode);
946
947     // Map the RHS.
948
//
949
Node fromRhs = getPropNode.getFirstChild().getNext().getNext();
950     JsExpression toRhs = mapExpression(fromRhs);
951
952     return new JsBinaryOperation(JsBinaryOperator.ASG, lhs, toRhs);
953   }
954
955   private JsExpression mapShiftVariant(Node shiftNode) throws JsParserException {
956     switch (shiftNode.getIntDatum()) {
957       case TokenStream.LSH:
958         return mapBinaryOperation(JsBinaryOperator.SHL, shiftNode);
959
960       case TokenStream.RSH:
961         return mapBinaryOperation(JsBinaryOperator.SHR, shiftNode);
962
963       case TokenStream.URSH:
964         return mapBinaryOperation(JsBinaryOperator.SHRU, shiftNode);
965
966       default:
967         throw new JsParserException("Unknown equality operator variant: "
968             + shiftNode.getIntDatum());
969     }
970   }
971
972   private JsStatement mapStatement(Node nodeStmt) throws JsParserException {
973     JsNode unknown = map(nodeStmt);
974     if (unknown != null) {
975       if (unknown instanceof JsStatement) {
976         return (JsStatement) unknown;
977       } else if (unknown instanceof JsExpression) {
978         return ((JsExpression) unknown).makeStmt();
979       } else {
980         throw createParserException("Expecting a statement", nodeStmt);
981       }
982     } else {
983       // When map() returns null, we return an empty statement.
984
//
985
return program.getEmptyStmt();
986     }
987   }
988
989   private void mapStatements(JsStatements stmts, Node nodeStmts)
990       throws JsParserException {
991     Node curr = nodeStmts.getFirstChild();
992     while (curr != null) {
993       JsStatement stmt = mapStatement(curr);
994       if (stmt != null) {
995         stmts.add(stmt);
996       } else {
997         // When mapStatement() returns null, we just ignore it.
998
//
999
}
1000      curr = curr.getNext();
1001    }
1002  }
1003
1004  private JsStatements mapStatements(Node nodeStmts) throws JsParserException {
1005    JsStatements stmts = new JsStatements();
1006    mapStatements(stmts, nodeStmts);
1007    return stmts;
1008  }
1009
1010  private JsSwitch mapSwitchStatement(Node switchNode) throws JsParserException {
1011    JsSwitch toSwitch = new JsSwitch();
1012
1013    // The switch expression.
1014
//
1015
Node fromSwitchExpr = switchNode.getFirstChild();
1016    toSwitch.setExpr(mapExpression(fromSwitchExpr));
1017
1018    // The members.
1019
//
1020
Node fromMember = fromSwitchExpr.getNext();
1021    while (fromMember != null) {
1022      if (fromMember.getType() == TokenStream.CASE) {
1023        JsCase toCase = new JsCase();
1024
1025        // Set the case expression. In JS, this can be any expression.
1026
//
1027
Node fromCaseExpr = fromMember.getFirstChild();
1028        toCase.setCaseExpr(mapExpression(fromCaseExpr));
1029
1030        // Set the case statements.
1031
//
1032
Node fromCaseBlock = fromCaseExpr.getNext();
1033        mapStatements(toCase.getStmts(), fromCaseBlock);
1034
1035        // Attach the case to the switch.
1036
//
1037
toSwitch.getCases().add(toCase);
1038      } else {
1039        // This should be the only default statement.
1040
// If more than one is present, we keep the last one.
1041
//
1042
assert (fromMember.getType() == TokenStream.DEFAULT);
1043        JsDefault toDefault = new JsDefault();
1044
1045        // Set the default statements.
1046
//
1047
Node fromDefaultBlock = fromMember.getFirstChild();
1048        mapStatements(toDefault.getStmts(), fromDefaultBlock);
1049
1050        // Attach the default to the switch.
1051
//
1052
toSwitch.getCases().add(toDefault);
1053      }
1054      fromMember = fromMember.getNext();
1055    }
1056
1057    return toSwitch;
1058  }
1059
1060  private JsThrow mapThrowStatement(Node throwNode) throws JsParserException {
1061    // Create, map, and attach.
1062
//
1063
Node fromExpr = throwNode.getFirstChild();
1064    JsThrow toThrow = new JsThrow(mapExpression(fromExpr));
1065    return toThrow;
1066  }
1067
1068  private JsTry mapTryStatement(Node tryNode) throws JsParserException {
1069    JsTry toTry = new JsTry();
1070
1071    // Map the "try" body.
1072
//
1073
Node fromTryBody = tryNode.getFirstChild();
1074    toTry.setTryBlock(mapBlock(fromTryBody));
1075
1076    // Map zero or more catch blocks.
1077
//
1078
Node fromCatchNodes = fromTryBody.getNext();
1079    Node fromCatchNode = fromCatchNodes.getFirstChild();
1080    while (fromCatchNode != null) {
1081      assert (fromCatchNode.getType() == TokenStream.CATCH);
1082      // Map the catch variable.
1083
//
1084
Node fromCatchVarName = fromCatchNode.getFirstChild();
1085      JsCatch catchBlock = new JsCatch(getScope(), fromCatchVarName.getString());
1086
1087      // Pre-advance to the next catch block, if any.
1088
// We do this here to decide whether or not this is the last one.
1089
//
1090
fromCatchNode = fromCatchNode.getNext();
1091
1092      // Map the condition, with a little fixup based on whether or not
1093
// this is the last catch block.
1094
//
1095
Node fromCondition = fromCatchVarName.getNext();
1096      JsExpression toCondition = mapExpression(fromCondition);
1097      catchBlock.setCondition(toCondition);
1098      if (fromCatchNode == null) {
1099        if (toCondition instanceof JsBooleanLiteral) {
1100          if (((JsBooleanLiteral) toCondition).getValue()) {
1101            // Actually, this is an unconditional catch block.
1102
// Indicate that by nulling the condition.
1103
//
1104
catchBlock.setCondition(null);
1105          }
1106        }
1107      }
1108
1109      // Map the catch body.
1110
//
1111
Node fromCatchBody = fromCondition.getNext();
1112      pushScope(catchBlock.getScope());
1113      catchBlock.setBody(mapBlock(fromCatchBody));
1114      popScope();
1115
1116      // Attach it.
1117
//
1118
toTry.getCatches().add(catchBlock);
1119    }
1120
1121    Node fromFinallyNode = fromCatchNodes.getNext();
1122    if (fromFinallyNode != null) {
1123      toTry.setFinallyBlock(mapBlock(fromFinallyNode));
1124    }
1125
1126    return toTry;
1127  }
1128
1129  private JsExpression mapUnaryVariant(Node unOp) throws JsParserException {
1130    switch (unOp.getIntDatum()) {
1131      case TokenStream.SUB:
1132        return mapPrefixOperation(JsUnaryOperator.NEG, unOp);
1133
1134      case TokenStream.NOT:
1135        return mapPrefixOperation(JsUnaryOperator.NOT, unOp);
1136
1137      case TokenStream.BITNOT:
1138        return mapPrefixOperation(JsUnaryOperator.BIT_NOT, unOp);
1139
1140      case TokenStream.TYPEOF:
1141        return mapPrefixOperation(JsUnaryOperator.TYPEOF, unOp);
1142
1143      case TokenStream.ADD:
1144        // Pretend we didn't see it.
1145
return mapExpression(unOp.getFirstChild());
1146
1147      case TokenStream.VOID:
1148        return mapPrefixOperation(JsUnaryOperator.VOID, unOp);
1149
1150      default:
1151        throw new JsParserException("Unknown unary operator variant: "
1152            + unOp.getIntDatum());
1153    }
1154  }
1155
1156  private JsVars mapVar(Node varNode) throws JsParserException {
1157    JsVars toVars = new JsVars();
1158    Node fromVar = varNode.getFirstChild();
1159    while (fromVar != null) {
1160      // Use a conservative name allocation strategy that allocates all names
1161
// from the function's scope, even the names of properties in field
1162
// literals.
1163
//
1164
String JavaDoc fromName = fromVar.getString();
1165      JsName toName = getScope().declareName(fromName);
1166      JsVars.JsVar toVar = new JsVars.JsVar(toName);
1167
1168      Node fromInit = fromVar.getFirstChild();
1169      if (fromInit != null) {
1170        JsExpression toInit = mapExpression(fromInit);
1171        toVar.setInitExpr(toInit);
1172      }
1173      toVars.add(toVar);
1174
1175      fromVar = fromVar.getNext();
1176    }
1177
1178    return toVars;
1179  }
1180
1181  private JsNode mapWithStatement(Node withNode) throws JsParserException {
1182    // The "with" statement is unsupported because it introduces ambiguity
1183
// related to whether or not a name is obfuscatable that we cannot resolve
1184
// statically. This is modified in our copy of the Rhino Parser to provide
1185
// detailed source & line info. So, this method should never actually be
1186
// called.
1187
//
1188
throw createParserException("Internal error: unexpected token 'with'",
1189        withNode);
1190  }
1191
1192  private void popScope() {
1193    scopeStack.pop();
1194  }
1195
1196  private void pushScope(JsScope scope) {
1197    scopeStack.push(scope);
1198  }
1199}
1200
Popular Tags