0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1999.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Mike Ang
0026: * Igor Bukanov
0027: * Yuh-Ruey Chen
0028: * Ethan Hugg
0029: * Bob Jervis
0030: * Terry Lucas
0031: * Mike McCabe
0032: * Milen Nankov
0033: * Norris Boyd
0034: *
0035: * Alternatively, the contents of this file may be used under the terms of
0036: * the GNU General Public License Version 2 or later (the "GPL"), in which
0037: * case the provisions of the GPL are applicable instead of those above. If
0038: * you wish to allow use of your version of this file only under the terms of
0039: * the GPL and not to allow others to use your version of this file under the
0040: * MPL, indicate your decision by deleting the provisions above and replacing
0041: * them with the notice and other provisions required by the GPL. If you do
0042: * not delete the provisions above, a recipient may use your version of this
0043: * file under either the MPL or the GPL.
0044: *
0045: * ***** END LICENSE BLOCK ***** */
0046:
0047: package org.mozilla.javascript;
0048:
0049: import java.io.Reader;
0050: import java.io.IOException;
0051: import java.util.Hashtable;
0052:
0053: /**
0054: * This class implements the JavaScript parser.
0055: *
0056: * It is based on the C source files jsparse.c and jsparse.h
0057: * in the jsref package.
0058: *
0059: * @see TokenStream
0060: *
0061: * @author Mike McCabe
0062: * @author Brendan Eich
0063: */
0064:
0065: public class Parser {
0066: // TokenInformation flags : currentFlaggedToken stores them together
0067: // with token type
0068: final static int CLEAR_TI_MASK = 0xFFFF, // mask to clear token information bits
0069: TI_AFTER_EOL = 1 << 16, // first token of the source line
0070: TI_CHECK_LABEL = 1 << 17; // indicates to check for label
0071:
0072: CompilerEnvirons compilerEnv;
0073: private ErrorReporter errorReporter;
0074: private String sourceURI;
0075: boolean calledByCompileFunction;
0076:
0077: private TokenStream ts;
0078: private int currentFlaggedToken;
0079: private int syntaxErrorCount;
0080:
0081: private IRFactory nf;
0082:
0083: private int nestingOfFunction;
0084:
0085: private Decompiler decompiler;
0086: private String encodedSource;
0087:
0088: // The following are per function variables and should be saved/restored
0089: // during function parsing.
0090: // XXX Move to separated class?
0091: ScriptOrFnNode currentScriptOrFn;
0092: Node.Scope currentScope;
0093: private int nestingOfWith;
0094: private Hashtable labelSet; // map of label names into nodes
0095: private ObjArray loopSet;
0096: private ObjArray loopAndSwitchSet;
0097: private boolean hasReturnValue;
0098: private int endFlags;
0099:
0100: // end of per function variables
0101:
0102: public int getCurrentLineNumber() {
0103: return ts.getLineno();
0104: }
0105:
0106: // Exception to unwind
0107: private static class ParserException extends RuntimeException {
0108: static final long serialVersionUID = 5882582646773765630L;
0109: }
0110:
0111: public Parser(CompilerEnvirons compilerEnv,
0112: ErrorReporter errorReporter) {
0113: this .compilerEnv = compilerEnv;
0114: this .errorReporter = errorReporter;
0115: }
0116:
0117: protected Decompiler createDecompiler(CompilerEnvirons compilerEnv) {
0118: return new Decompiler();
0119: }
0120:
0121: void addStrictWarning(String messageId, String messageArg) {
0122: if (compilerEnv.isStrictMode())
0123: addWarning(messageId, messageArg);
0124: }
0125:
0126: void addWarning(String messageId, String messageArg) {
0127: String message = ScriptRuntime.getMessage1(messageId,
0128: messageArg);
0129: if (compilerEnv.reportWarningAsError()) {
0130: ++syntaxErrorCount;
0131: errorReporter.error(message, sourceURI, ts.getLineno(), ts
0132: .getLine(), ts.getOffset());
0133: } else
0134: errorReporter.warning(message, sourceURI, ts.getLineno(),
0135: ts.getLine(), ts.getOffset());
0136: }
0137:
0138: void addError(String messageId) {
0139: ++syntaxErrorCount;
0140: String message = ScriptRuntime.getMessage0(messageId);
0141: errorReporter.error(message, sourceURI, ts.getLineno(), ts
0142: .getLine(), ts.getOffset());
0143: }
0144:
0145: void addError(String messageId, String messageArg) {
0146: ++syntaxErrorCount;
0147: String message = ScriptRuntime.getMessage1(messageId,
0148: messageArg);
0149: errorReporter.error(message, sourceURI, ts.getLineno(), ts
0150: .getLine(), ts.getOffset());
0151: }
0152:
0153: RuntimeException reportError(String messageId) {
0154: addError(messageId);
0155:
0156: // Throw a ParserException exception to unwind the recursive descent
0157: // parse.
0158: throw new ParserException();
0159: }
0160:
0161: private int peekToken() throws IOException {
0162: int tt = currentFlaggedToken;
0163: if (tt == Token.EOF) {
0164: tt = ts.getToken();
0165: if (tt == Token.EOL) {
0166: do {
0167: tt = ts.getToken();
0168: } while (tt == Token.EOL);
0169: tt |= TI_AFTER_EOL;
0170: }
0171: currentFlaggedToken = tt;
0172: }
0173: return tt & CLEAR_TI_MASK;
0174: }
0175:
0176: private int peekFlaggedToken() throws IOException {
0177: peekToken();
0178: return currentFlaggedToken;
0179: }
0180:
0181: private void consumeToken() {
0182: currentFlaggedToken = Token.EOF;
0183: }
0184:
0185: private int nextToken() throws IOException {
0186: int tt = peekToken();
0187: consumeToken();
0188: return tt;
0189: }
0190:
0191: private int nextFlaggedToken() throws IOException {
0192: peekToken();
0193: int ttFlagged = currentFlaggedToken;
0194: consumeToken();
0195: return ttFlagged;
0196: }
0197:
0198: private boolean matchToken(int toMatch) throws IOException {
0199: int tt = peekToken();
0200: if (tt != toMatch) {
0201: return false;
0202: }
0203: consumeToken();
0204: return true;
0205: }
0206:
0207: private int peekTokenOrEOL() throws IOException {
0208: int tt = peekToken();
0209: // Check for last peeked token flags
0210: if ((currentFlaggedToken & TI_AFTER_EOL) != 0) {
0211: tt = Token.EOL;
0212: }
0213: return tt;
0214: }
0215:
0216: private void setCheckForLabel() {
0217: if ((currentFlaggedToken & CLEAR_TI_MASK) != Token.NAME)
0218: throw Kit.codeBug();
0219: currentFlaggedToken |= TI_CHECK_LABEL;
0220: }
0221:
0222: private void mustMatchToken(int toMatch, String messageId)
0223: throws IOException, ParserException {
0224: if (!matchToken(toMatch)) {
0225: reportError(messageId);
0226: }
0227: }
0228:
0229: private void mustHaveXML() {
0230: if (!compilerEnv.isXmlAvailable()) {
0231: reportError("msg.XML.not.available");
0232: }
0233: }
0234:
0235: public String getEncodedSource() {
0236: return encodedSource;
0237: }
0238:
0239: public boolean eof() {
0240: return ts.eof();
0241: }
0242:
0243: boolean insideFunction() {
0244: return nestingOfFunction != 0;
0245: }
0246:
0247: void pushScope(Node node) {
0248: Node.Scope scopeNode = (Node.Scope) node;
0249: if (scopeNode.getParentScope() != null)
0250: throw Kit.codeBug();
0251: scopeNode.setParent(currentScope);
0252: currentScope = scopeNode;
0253: }
0254:
0255: void popScope() {
0256: currentScope = currentScope.getParentScope();
0257: }
0258:
0259: private Node enterLoop(Node loopLabel, boolean doPushScope) {
0260: Node loop = nf.createLoopNode(loopLabel, ts.getLineno());
0261: if (loopSet == null) {
0262: loopSet = new ObjArray();
0263: if (loopAndSwitchSet == null) {
0264: loopAndSwitchSet = new ObjArray();
0265: }
0266: }
0267: loopSet.push(loop);
0268: loopAndSwitchSet.push(loop);
0269: if (doPushScope) {
0270: pushScope(loop);
0271: }
0272: return loop;
0273: }
0274:
0275: private void exitLoop(boolean doPopScope) {
0276: loopSet.pop();
0277: loopAndSwitchSet.pop();
0278: if (doPopScope) {
0279: popScope();
0280: }
0281: }
0282:
0283: private Node enterSwitch(Node switchSelector, int lineno) {
0284: Node switchNode = nf.createSwitch(switchSelector, lineno);
0285: if (loopAndSwitchSet == null) {
0286: loopAndSwitchSet = new ObjArray();
0287: }
0288: loopAndSwitchSet.push(switchNode);
0289: return switchNode;
0290: }
0291:
0292: private void exitSwitch() {
0293: loopAndSwitchSet.pop();
0294: }
0295:
0296: /*
0297: * Build a parse tree from the given sourceString.
0298: *
0299: * @return an Object representing the parsed
0300: * program. If the parse fails, null will be returned. (The
0301: * parse failure will result in a call to the ErrorReporter from
0302: * CompilerEnvirons.)
0303: */
0304: public ScriptOrFnNode parse(String sourceString, String sourceURI,
0305: int lineno) {
0306: this .sourceURI = sourceURI;
0307: this .ts = new TokenStream(this , null, sourceString, lineno);
0308: try {
0309: return parse();
0310: } catch (IOException ex) {
0311: // Should never happen
0312: throw new IllegalStateException();
0313: }
0314: }
0315:
0316: /*
0317: * Build a parse tree from the given sourceString.
0318: *
0319: * @return an Object representing the parsed
0320: * program. If the parse fails, null will be returned. (The
0321: * parse failure will result in a call to the ErrorReporter from
0322: * CompilerEnvirons.)
0323: */
0324: public ScriptOrFnNode parse(Reader sourceReader, String sourceURI,
0325: int lineno) throws IOException {
0326: this .sourceURI = sourceURI;
0327: this .ts = new TokenStream(this , sourceReader, null, lineno);
0328: return parse();
0329: }
0330:
0331: private ScriptOrFnNode parse() throws IOException {
0332: this .decompiler = createDecompiler(compilerEnv);
0333: this .nf = new IRFactory(this );
0334: currentScriptOrFn = nf.createScript();
0335: currentScope = currentScriptOrFn;
0336: int sourceStartOffset = decompiler.getCurrentOffset();
0337: this .encodedSource = null;
0338: decompiler.addToken(Token.SCRIPT);
0339:
0340: this .currentFlaggedToken = Token.EOF;
0341: this .syntaxErrorCount = 0;
0342:
0343: int baseLineno = ts.getLineno(); // line number where source starts
0344:
0345: /* so we have something to add nodes to until
0346: * we've collected all the source */
0347: Node pn = nf.createLeaf(Token.BLOCK);
0348:
0349: try {
0350: for (;;) {
0351: int tt = peekToken();
0352:
0353: if (tt <= Token.EOF) {
0354: break;
0355: }
0356:
0357: Node n;
0358: if (tt == Token.FUNCTION) {
0359: consumeToken();
0360: try {
0361: n = function(calledByCompileFunction ? FunctionNode.FUNCTION_EXPRESSION
0362: : FunctionNode.FUNCTION_STATEMENT);
0363: } catch (ParserException e) {
0364: break;
0365: }
0366: } else {
0367: n = statement();
0368: }
0369: nf.addChildToBack(pn, n);
0370: }
0371: } catch (StackOverflowError ex) {
0372: String msg = ScriptRuntime
0373: .getMessage0("msg.too.deep.parser.recursion");
0374: throw Context.reportRuntimeError(msg, sourceURI, ts
0375: .getLineno(), null, 0);
0376: }
0377:
0378: if (this .syntaxErrorCount != 0) {
0379: String msg = String.valueOf(this .syntaxErrorCount);
0380: msg = ScriptRuntime.getMessage1("msg.got.syntax.errors",
0381: msg);
0382: throw errorReporter.runtimeError(msg, sourceURI,
0383: baseLineno, null, 0);
0384: }
0385:
0386: currentScriptOrFn.setSourceName(sourceURI);
0387: currentScriptOrFn.setBaseLineno(baseLineno);
0388: currentScriptOrFn.setEndLineno(ts.getLineno());
0389:
0390: int sourceEndOffset = decompiler.getCurrentOffset();
0391: currentScriptOrFn.setEncodedSourceBounds(sourceStartOffset,
0392: sourceEndOffset);
0393:
0394: nf.initScript(currentScriptOrFn, pn);
0395:
0396: if (compilerEnv.isGeneratingSource()) {
0397: encodedSource = decompiler.getEncodedSource();
0398: }
0399: this .decompiler = null; // It helps GC
0400:
0401: return currentScriptOrFn;
0402: }
0403:
0404: /*
0405: * The C version of this function takes an argument list,
0406: * which doesn't seem to be needed for tree generation...
0407: * it'd only be useful for checking argument hiding, which
0408: * I'm not doing anyway...
0409: */
0410: private Node parseFunctionBody() throws IOException {
0411: ++nestingOfFunction;
0412: Node pn = nf.createBlock(ts.getLineno());
0413: try {
0414: bodyLoop: for (;;) {
0415: Node n;
0416: int tt = peekToken();
0417: switch (tt) {
0418: case Token.ERROR:
0419: case Token.EOF:
0420: case Token.RC:
0421: break bodyLoop;
0422:
0423: case Token.FUNCTION:
0424: consumeToken();
0425: n = function(FunctionNode.FUNCTION_STATEMENT);
0426: break;
0427: default:
0428: n = statement();
0429: break;
0430: }
0431: nf.addChildToBack(pn, n);
0432: }
0433: } catch (ParserException e) {
0434: // Ignore it
0435: } finally {
0436: --nestingOfFunction;
0437: }
0438:
0439: return pn;
0440: }
0441:
0442: private Node function(int functionType) throws IOException,
0443: ParserException {
0444: int syntheticType = functionType;
0445: int baseLineno = ts.getLineno(); // line number where source starts
0446:
0447: int functionSourceStart = decompiler
0448: .markFunctionStart(functionType);
0449: String name;
0450: Node memberExprNode = null;
0451: if (matchToken(Token.NAME)) {
0452: name = ts.getString();
0453: decompiler.addName(name);
0454: if (!matchToken(Token.LP)) {
0455: if (compilerEnv.isAllowMemberExprAsFunctionName()) {
0456: // Extension to ECMA: if 'function <name>' does not follow
0457: // by '(', assume <name> starts memberExpr
0458: Node memberExprHead = nf.createName(name);
0459: name = "";
0460: memberExprNode = memberExprTail(false,
0461: memberExprHead);
0462: }
0463: mustMatchToken(Token.LP, "msg.no.paren.parms");
0464: }
0465: } else if (matchToken(Token.LP)) {
0466: // Anonymous function
0467: name = "";
0468: } else {
0469: name = "";
0470: if (compilerEnv.isAllowMemberExprAsFunctionName()) {
0471: // Note that memberExpr can not start with '(' like
0472: // in function (1+2).toString(), because 'function (' already
0473: // processed as anonymous function
0474: memberExprNode = memberExpr(false);
0475: }
0476: mustMatchToken(Token.LP, "msg.no.paren.parms");
0477: }
0478:
0479: if (memberExprNode != null) {
0480: syntheticType = FunctionNode.FUNCTION_EXPRESSION;
0481: }
0482:
0483: if (syntheticType != FunctionNode.FUNCTION_EXPRESSION
0484: && name.length() > 0) {
0485: // Function statements define a symbol in the enclosing scope
0486: defineSymbol(Token.FUNCTION, name);
0487: }
0488:
0489: boolean nested = insideFunction();
0490:
0491: FunctionNode fnNode = nf.createFunction(name);
0492: if (nested || nestingOfWith > 0) {
0493: // 1. Nested functions are not affected by the dynamic scope flag
0494: // as dynamic scope is already a parent of their scope.
0495: // 2. Functions defined under the with statement also immune to
0496: // this setup, in which case dynamic scope is ignored in favor
0497: // of with object.
0498: fnNode.itsIgnoreDynamicScope = true;
0499: }
0500: int functionIndex = currentScriptOrFn.addFunction(fnNode);
0501:
0502: int functionSourceEnd;
0503:
0504: ScriptOrFnNode savedScriptOrFn = currentScriptOrFn;
0505: currentScriptOrFn = fnNode;
0506: Node.Scope savedCurrentScope = currentScope;
0507: currentScope = fnNode;
0508: int savedNestingOfWith = nestingOfWith;
0509: nestingOfWith = 0;
0510: Hashtable savedLabelSet = labelSet;
0511: labelSet = null;
0512: ObjArray savedLoopSet = loopSet;
0513: loopSet = null;
0514: ObjArray savedLoopAndSwitchSet = loopAndSwitchSet;
0515: loopAndSwitchSet = null;
0516: boolean savedHasReturnValue = hasReturnValue;
0517: int savedFunctionEndFlags = endFlags;
0518:
0519: Node destructuring = null;
0520: Node body;
0521: try {
0522: decompiler.addToken(Token.LP);
0523: if (!matchToken(Token.RP)) {
0524: boolean first = true;
0525: do {
0526: if (!first)
0527: decompiler.addToken(Token.COMMA);
0528: first = false;
0529: int tt = peekToken();
0530: if (tt == Token.LB || tt == Token.LC) {
0531: // Destructuring assignment for parameters: add a
0532: // dummy parameter name, and add a statement to the
0533: // body to initialize variables from the destructuring
0534: // assignment
0535: if (destructuring == null) {
0536: destructuring = new Node(Token.COMMA);
0537: }
0538: String parmName = currentScriptOrFn
0539: .getNextTempName();
0540: defineSymbol(Token.LP, parmName);
0541: destructuring.addChildToBack(nf
0542: .createDestructuringAssignment(
0543: Token.VAR, primaryExpr(), nf
0544: .createName(parmName)));
0545: } else {
0546: mustMatchToken(Token.NAME, "msg.no.parm");
0547: String s = ts.getString();
0548: defineSymbol(Token.LP, s);
0549: decompiler.addName(s);
0550: }
0551: } while (matchToken(Token.COMMA));
0552:
0553: mustMatchToken(Token.RP, "msg.no.paren.after.parms");
0554: }
0555: decompiler.addToken(Token.RP);
0556:
0557: mustMatchToken(Token.LC, "msg.no.brace.body");
0558: decompiler.addEOL(Token.LC);
0559: body = parseFunctionBody();
0560: if (destructuring != null) {
0561: body.addChildToFront(new Node(Token.EXPR_VOID,
0562: destructuring, ts.getLineno()));
0563: }
0564: mustMatchToken(Token.RC, "msg.no.brace.after.body");
0565:
0566: if (compilerEnv.isStrictMode()
0567: && !body.hasConsistentReturnUsage()) {
0568: String msg = name.length() > 0 ? "msg.no.return.value"
0569: : "msg.anon.no.return.value";
0570: addStrictWarning(msg, name);
0571: }
0572:
0573: if (syntheticType == FunctionNode.FUNCTION_EXPRESSION
0574: && name.length() > 0
0575: && currentScope.getSymbol(name) == null) {
0576: // Function expressions define a name only in the body of the
0577: // function, and only if not hidden by a parameter name
0578: defineSymbol(Token.FUNCTION, name);
0579: }
0580:
0581: decompiler.addToken(Token.RC);
0582: functionSourceEnd = decompiler
0583: .markFunctionEnd(functionSourceStart);
0584: if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
0585: // Add EOL only if function is not part of expression
0586: // since it gets SEMI + EOL from Statement in that case
0587: decompiler.addToken(Token.EOL);
0588: }
0589: } finally {
0590: hasReturnValue = savedHasReturnValue;
0591: endFlags = savedFunctionEndFlags;
0592: loopAndSwitchSet = savedLoopAndSwitchSet;
0593: loopSet = savedLoopSet;
0594: labelSet = savedLabelSet;
0595: nestingOfWith = savedNestingOfWith;
0596: currentScriptOrFn = savedScriptOrFn;
0597: currentScope = savedCurrentScope;
0598: }
0599:
0600: fnNode.setEncodedSourceBounds(functionSourceStart,
0601: functionSourceEnd);
0602: fnNode.setSourceName(sourceURI);
0603: fnNode.setBaseLineno(baseLineno);
0604: fnNode.setEndLineno(ts.getLineno());
0605:
0606: Node pn = nf.initFunction(fnNode, functionIndex, body,
0607: syntheticType);
0608: if (memberExprNode != null) {
0609: pn = nf.createAssignment(Token.ASSIGN, memberExprNode, pn);
0610: if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
0611: // XXX check JScript behavior: should it be createExprStatement?
0612: pn = nf.createExprStatementNoReturn(pn, baseLineno);
0613: }
0614: }
0615: return pn;
0616: }
0617:
0618: private Node statements(Node scope) throws IOException {
0619: Node pn = scope != null ? scope : nf
0620: .createBlock(ts.getLineno());
0621:
0622: int tt;
0623: while ((tt = peekToken()) > Token.EOF && tt != Token.RC) {
0624: nf.addChildToBack(pn, statement());
0625: }
0626:
0627: return pn;
0628: }
0629:
0630: private Node condition() throws IOException, ParserException {
0631: mustMatchToken(Token.LP, "msg.no.paren.cond");
0632: decompiler.addToken(Token.LP);
0633: Node pn = expr(false);
0634: mustMatchToken(Token.RP, "msg.no.paren.after.cond");
0635: decompiler.addToken(Token.RP);
0636:
0637: // Report strict warning on code like "if (a = 7) ...". Suppress the
0638: // warning if the condition is parenthesized, like "if ((a = 7)) ...".
0639: if (pn.getProp(Node.PARENTHESIZED_PROP) == null
0640: && (pn.getType() == Token.SETNAME
0641: || pn.getType() == Token.SETPROP || pn
0642: .getType() == Token.SETELEM)) {
0643: addStrictWarning("msg.equal.as.assign", "");
0644: }
0645: return pn;
0646: }
0647:
0648: // match a NAME; return null if no match.
0649: private Node matchJumpLabelName() throws IOException,
0650: ParserException {
0651: Node label = null;
0652:
0653: int tt = peekTokenOrEOL();
0654: if (tt == Token.NAME) {
0655: consumeToken();
0656: String name = ts.getString();
0657: decompiler.addName(name);
0658: if (labelSet != null) {
0659: label = (Node) labelSet.get(name);
0660: }
0661: if (label == null) {
0662: reportError("msg.undef.label");
0663: }
0664: }
0665:
0666: return label;
0667: }
0668:
0669: private Node statement() throws IOException {
0670: try {
0671: Node pn = statementHelper(null);
0672: if (pn != null) {
0673: if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
0674: addStrictWarning("msg.no.side.effects", "");
0675: return pn;
0676: }
0677: } catch (ParserException e) {
0678: }
0679:
0680: // skip to end of statement
0681: int lineno = ts.getLineno();
0682: guessingStatementEnd: for (;;) {
0683: int tt = peekTokenOrEOL();
0684: consumeToken();
0685: switch (tt) {
0686: case Token.ERROR:
0687: case Token.EOF:
0688: case Token.EOL:
0689: case Token.SEMI:
0690: break guessingStatementEnd;
0691: }
0692: }
0693: return nf.createExprStatement(nf.createName("error"), lineno);
0694: }
0695:
0696: private Node statementHelper(Node statementLabel)
0697: throws IOException, ParserException {
0698: Node pn = null;
0699: int tt = peekToken();
0700:
0701: switch (tt) {
0702: case Token.IF: {
0703: consumeToken();
0704:
0705: decompiler.addToken(Token.IF);
0706: int lineno = ts.getLineno();
0707: Node cond = condition();
0708: decompiler.addEOL(Token.LC);
0709: Node ifTrue = statement();
0710: Node ifFalse = null;
0711: if (matchToken(Token.ELSE)) {
0712: decompiler.addToken(Token.RC);
0713: decompiler.addToken(Token.ELSE);
0714: decompiler.addEOL(Token.LC);
0715: ifFalse = statement();
0716: }
0717: decompiler.addEOL(Token.RC);
0718: pn = nf.createIf(cond, ifTrue, ifFalse, lineno);
0719: return pn;
0720: }
0721:
0722: case Token.SWITCH: {
0723: consumeToken();
0724:
0725: decompiler.addToken(Token.SWITCH);
0726: int lineno = ts.getLineno();
0727: mustMatchToken(Token.LP, "msg.no.paren.switch");
0728: decompiler.addToken(Token.LP);
0729: pn = enterSwitch(expr(false), lineno);
0730: try {
0731: mustMatchToken(Token.RP, "msg.no.paren.after.switch");
0732: decompiler.addToken(Token.RP);
0733: mustMatchToken(Token.LC, "msg.no.brace.switch");
0734: decompiler.addEOL(Token.LC);
0735:
0736: boolean hasDefault = false;
0737: switchLoop: for (;;) {
0738: tt = nextToken();
0739: Node caseExpression;
0740: switch (tt) {
0741: case Token.RC:
0742: break switchLoop;
0743:
0744: case Token.CASE:
0745: decompiler.addToken(Token.CASE);
0746: caseExpression = expr(false);
0747: mustMatchToken(Token.COLON, "msg.no.colon.case");
0748: decompiler.addEOL(Token.COLON);
0749: break;
0750:
0751: case Token.DEFAULT:
0752: if (hasDefault) {
0753: reportError("msg.double.switch.default");
0754: }
0755: decompiler.addToken(Token.DEFAULT);
0756: hasDefault = true;
0757: caseExpression = null;
0758: mustMatchToken(Token.COLON, "msg.no.colon.case");
0759: decompiler.addEOL(Token.COLON);
0760: break;
0761:
0762: default:
0763: reportError("msg.bad.switch");
0764: break switchLoop;
0765: }
0766:
0767: Node block = nf.createLeaf(Token.BLOCK);
0768: while ((tt = peekToken()) != Token.RC
0769: && tt != Token.CASE && tt != Token.DEFAULT
0770: && tt != Token.EOF) {
0771: nf.addChildToBack(block, statement());
0772: }
0773:
0774: // caseExpression == null => add default label
0775: nf.addSwitchCase(pn, caseExpression, block);
0776: }
0777: decompiler.addEOL(Token.RC);
0778: nf.closeSwitch(pn);
0779: } finally {
0780: exitSwitch();
0781: }
0782: return pn;
0783: }
0784:
0785: case Token.WHILE: {
0786: consumeToken();
0787: decompiler.addToken(Token.WHILE);
0788:
0789: Node loop = enterLoop(statementLabel, true);
0790: try {
0791: Node cond = condition();
0792: decompiler.addEOL(Token.LC);
0793: Node body = statement();
0794: decompiler.addEOL(Token.RC);
0795: pn = nf.createWhile(loop, cond, body);
0796: } finally {
0797: exitLoop(true);
0798: }
0799: return pn;
0800: }
0801:
0802: case Token.DO: {
0803: consumeToken();
0804: decompiler.addToken(Token.DO);
0805: decompiler.addEOL(Token.LC);
0806:
0807: Node loop = enterLoop(statementLabel, true);
0808: try {
0809: Node body = statement();
0810: decompiler.addToken(Token.RC);
0811: mustMatchToken(Token.WHILE, "msg.no.while.do");
0812: decompiler.addToken(Token.WHILE);
0813: Node cond = condition();
0814: pn = nf.createDoWhile(loop, body, cond);
0815: } finally {
0816: exitLoop(true);
0817: }
0818: // Always auto-insert semicolon to follow SpiderMonkey:
0819: // It is required by ECMAScript but is ignored by the rest of
0820: // world, see bug 238945
0821: matchToken(Token.SEMI);
0822: decompiler.addEOL(Token.SEMI);
0823: return pn;
0824: }
0825:
0826: case Token.FOR: {
0827: consumeToken();
0828: boolean isForEach = false;
0829: decompiler.addToken(Token.FOR);
0830:
0831: Node loop = enterLoop(statementLabel, true);
0832: try {
0833: Node init; // Node init is also foo in 'foo in object'
0834: Node cond; // Node cond is also object in 'foo in object'
0835: Node incr = null;
0836: Node body;
0837: int declType = -1;
0838:
0839: // See if this is a for each () instead of just a for ()
0840: if (matchToken(Token.NAME)) {
0841: decompiler.addName(ts.getString());
0842: if (ts.getString().equals("each")) {
0843: isForEach = true;
0844: } else {
0845: reportError("msg.no.paren.for");
0846: }
0847: }
0848:
0849: mustMatchToken(Token.LP, "msg.no.paren.for");
0850: decompiler.addToken(Token.LP);
0851: tt = peekToken();
0852: if (tt == Token.SEMI) {
0853: init = nf.createLeaf(Token.EMPTY);
0854: } else {
0855: if (tt == Token.VAR || tt == Token.LET) {
0856: // set init to a var list or initial
0857: consumeToken(); // consume the token
0858: decompiler.addToken(tt);
0859: init = variables(true, tt);
0860: declType = tt;
0861: } else {
0862: init = expr(true);
0863: }
0864: }
0865:
0866: if (matchToken(Token.IN)) {
0867: decompiler.addToken(Token.IN);
0868: // 'cond' is the object over which we're iterating
0869: cond = expr(false);
0870: } else { // ordinary for loop
0871: mustMatchToken(Token.SEMI, "msg.no.semi.for");
0872: decompiler.addToken(Token.SEMI);
0873: if (peekToken() == Token.SEMI) {
0874: // no loop condition
0875: cond = nf.createLeaf(Token.EMPTY);
0876: } else {
0877: cond = expr(false);
0878: }
0879:
0880: mustMatchToken(Token.SEMI, "msg.no.semi.for.cond");
0881: decompiler.addToken(Token.SEMI);
0882: if (peekToken() == Token.RP) {
0883: incr = nf.createLeaf(Token.EMPTY);
0884: } else {
0885: incr = expr(false);
0886: }
0887: }
0888:
0889: mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
0890: decompiler.addToken(Token.RP);
0891: decompiler.addEOL(Token.LC);
0892: body = statement();
0893: decompiler.addEOL(Token.RC);
0894:
0895: if (incr == null) {
0896: // cond could be null if 'in obj' got eaten
0897: // by the init node.
0898: pn = nf.createForIn(declType, loop, init, cond,
0899: body, isForEach);
0900: } else {
0901: pn = nf.createFor(loop, init, cond, incr, body);
0902: }
0903: } finally {
0904: exitLoop(true);
0905: }
0906: return pn;
0907: }
0908:
0909: case Token.TRY: {
0910: consumeToken();
0911: int lineno = ts.getLineno();
0912:
0913: Node tryblock;
0914: Node catchblocks = null;
0915: Node finallyblock = null;
0916:
0917: decompiler.addToken(Token.TRY);
0918: if (peekToken() != Token.LC) {
0919: reportError("msg.no.brace.try");
0920: }
0921: decompiler.addEOL(Token.LC);
0922: tryblock = statement();
0923: decompiler.addEOL(Token.RC);
0924:
0925: catchblocks = nf.createLeaf(Token.BLOCK);
0926:
0927: boolean sawDefaultCatch = false;
0928: int peek = peekToken();
0929: if (peek == Token.CATCH) {
0930: while (matchToken(Token.CATCH)) {
0931: if (sawDefaultCatch) {
0932: reportError("msg.catch.unreachable");
0933: }
0934: decompiler.addToken(Token.CATCH);
0935: mustMatchToken(Token.LP, "msg.no.paren.catch");
0936: decompiler.addToken(Token.LP);
0937:
0938: mustMatchToken(Token.NAME, "msg.bad.catchcond");
0939: String varName = ts.getString();
0940: decompiler.addName(varName);
0941:
0942: Node catchCond = null;
0943: if (matchToken(Token.IF)) {
0944: decompiler.addToken(Token.IF);
0945: catchCond = expr(false);
0946: } else {
0947: sawDefaultCatch = true;
0948: }
0949:
0950: mustMatchToken(Token.RP, "msg.bad.catchcond");
0951: decompiler.addToken(Token.RP);
0952: mustMatchToken(Token.LC, "msg.no.brace.catchblock");
0953: decompiler.addEOL(Token.LC);
0954:
0955: nf.addChildToBack(catchblocks, nf.createCatch(
0956: varName, catchCond, statements(null), ts
0957: .getLineno()));
0958:
0959: mustMatchToken(Token.RC, "msg.no.brace.after.body");
0960: decompiler.addEOL(Token.RC);
0961: }
0962: } else if (peek != Token.FINALLY) {
0963: mustMatchToken(Token.FINALLY, "msg.try.no.catchfinally");
0964: }
0965:
0966: if (matchToken(Token.FINALLY)) {
0967: decompiler.addToken(Token.FINALLY);
0968: decompiler.addEOL(Token.LC);
0969: finallyblock = statement();
0970: decompiler.addEOL(Token.RC);
0971: }
0972:
0973: pn = nf.createTryCatchFinally(tryblock, catchblocks,
0974: finallyblock, lineno);
0975:
0976: return pn;
0977: }
0978:
0979: case Token.THROW: {
0980: consumeToken();
0981: if (peekTokenOrEOL() == Token.EOL) {
0982: // ECMAScript does not allow new lines before throw expression,
0983: // see bug 256617
0984: reportError("msg.bad.throw.eol");
0985: }
0986:
0987: int lineno = ts.getLineno();
0988: decompiler.addToken(Token.THROW);
0989: pn = nf.createThrow(expr(false), lineno);
0990: break;
0991: }
0992:
0993: case Token.BREAK: {
0994: consumeToken();
0995: int lineno = ts.getLineno();
0996:
0997: decompiler.addToken(Token.BREAK);
0998:
0999: // matchJumpLabelName only matches if there is one
1000: Node breakStatement = matchJumpLabelName();
1001: if (breakStatement == null) {
1002: if (loopAndSwitchSet == null
1003: || loopAndSwitchSet.size() == 0) {
1004: reportError("msg.bad.break");
1005: return null;
1006: }
1007: breakStatement = (Node) loopAndSwitchSet.peek();
1008: }
1009: pn = nf.createBreak(breakStatement, lineno);
1010: break;
1011: }
1012:
1013: case Token.CONTINUE: {
1014: consumeToken();
1015: int lineno = ts.getLineno();
1016:
1017: decompiler.addToken(Token.CONTINUE);
1018:
1019: Node loop;
1020: // matchJumpLabelName only matches if there is one
1021: Node label = matchJumpLabelName();
1022: if (label == null) {
1023: if (loopSet == null || loopSet.size() == 0) {
1024: reportError("msg.continue.outside");
1025: return null;
1026: }
1027: loop = (Node) loopSet.peek();
1028: } else {
1029: loop = nf.getLabelLoop(label);
1030: if (loop == null) {
1031: reportError("msg.continue.nonloop");
1032: return null;
1033: }
1034: }
1035: pn = nf.createContinue(loop, lineno);
1036: break;
1037: }
1038:
1039: case Token.WITH: {
1040: consumeToken();
1041:
1042: decompiler.addToken(Token.WITH);
1043: int lineno = ts.getLineno();
1044: mustMatchToken(Token.LP, "msg.no.paren.with");
1045: decompiler.addToken(Token.LP);
1046: Node obj = expr(false);
1047: mustMatchToken(Token.RP, "msg.no.paren.after.with");
1048: decompiler.addToken(Token.RP);
1049: decompiler.addEOL(Token.LC);
1050:
1051: ++nestingOfWith;
1052: Node body;
1053: try {
1054: body = statement();
1055: } finally {
1056: --nestingOfWith;
1057: }
1058:
1059: decompiler.addEOL(Token.RC);
1060:
1061: pn = nf.createWith(obj, body, lineno);
1062: return pn;
1063: }
1064:
1065: case Token.CONST:
1066: case Token.VAR: {
1067: consumeToken();
1068: decompiler.addToken(tt);
1069: pn = variables(false, tt);
1070: break;
1071: }
1072:
1073: case Token.LET: {
1074: consumeToken();
1075: decompiler.addToken(Token.LET);
1076: if (peekToken() == Token.LP) {
1077: pn = let(true);
1078: } else {
1079: pn = variables(false, tt);
1080: }
1081: return pn;
1082: }
1083:
1084: case Token.RETURN:
1085: case Token.YIELD: {
1086: pn = returnOrYield(tt, false);
1087: break;
1088: }
1089:
1090: case Token.DEBUGGER:
1091: consumeToken();
1092: decompiler.addToken(Token.DEBUGGER);
1093: pn = nf.createDebugger(ts.getLineno());
1094: break;
1095:
1096: case Token.LC:
1097: consumeToken();
1098: if (statementLabel != null) {
1099: decompiler.addToken(Token.LC);
1100: }
1101: Node scope = nf
1102: .createScopeNode(Token.BLOCK, ts.getLineno());
1103: pushScope(scope);
1104: try {
1105: statements(scope);
1106: mustMatchToken(Token.RC, "msg.no.brace.block");
1107: if (statementLabel != null) {
1108: decompiler.addEOL(Token.RC);
1109: }
1110: return scope;
1111: } finally {
1112: popScope();
1113: }
1114:
1115: case Token.ERROR:
1116: // Fall thru, to have a node for error recovery to work on
1117: case Token.SEMI:
1118: consumeToken();
1119: pn = nf.createLeaf(Token.EMPTY);
1120: return pn;
1121:
1122: case Token.FUNCTION: {
1123: consumeToken();
1124: pn = function(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
1125: return pn;
1126: }
1127:
1128: case Token.DEFAULT:
1129: consumeToken();
1130: mustHaveXML();
1131:
1132: decompiler.addToken(Token.DEFAULT);
1133: int nsLine = ts.getLineno();
1134:
1135: if (!(matchToken(Token.NAME) && ts.getString()
1136: .equals("xml"))) {
1137: reportError("msg.bad.namespace");
1138: }
1139: decompiler.addName(" xml");
1140:
1141: if (!(matchToken(Token.NAME) && ts.getString().equals(
1142: "namespace"))) {
1143: reportError("msg.bad.namespace");
1144: }
1145: decompiler.addName(" namespace");
1146:
1147: if (!matchToken(Token.ASSIGN)) {
1148: reportError("msg.bad.namespace");
1149: }
1150: decompiler.addToken(Token.ASSIGN);
1151:
1152: Node expr = expr(false);
1153: pn = nf.createDefaultNamespace(expr, nsLine);
1154: break;
1155:
1156: case Token.NAME: {
1157: int lineno = ts.getLineno();
1158: String name = ts.getString();
1159: setCheckForLabel();
1160: pn = expr(false);
1161: if (pn.getType() != Token.LABEL) {
1162: pn = nf.createExprStatement(pn, lineno);
1163: } else {
1164: // Parsed the label: push back token should be
1165: // colon that primaryExpr left untouched.
1166: if (peekToken() != Token.COLON)
1167: Kit.codeBug();
1168: consumeToken();
1169: // depend on decompiling lookahead to guess that that
1170: // last name was a label.
1171: decompiler.addName(name);
1172: decompiler.addEOL(Token.COLON);
1173:
1174: if (labelSet == null) {
1175: labelSet = new Hashtable();
1176: } else if (labelSet.containsKey(name)) {
1177: reportError("msg.dup.label");
1178: }
1179:
1180: boolean firstLabel;
1181: if (statementLabel == null) {
1182: firstLabel = true;
1183: statementLabel = pn;
1184: } else {
1185: // Discard multiple label nodes and use only
1186: // the first: it allows to simplify IRFactory
1187: firstLabel = false;
1188: }
1189: labelSet.put(name, statementLabel);
1190: try {
1191: pn = statementHelper(statementLabel);
1192: } finally {
1193: labelSet.remove(name);
1194: }
1195: if (firstLabel) {
1196: pn = nf.createLabeledStatement(statementLabel, pn);
1197: }
1198: return pn;
1199: }
1200: break;
1201: }
1202:
1203: default: {
1204: int lineno = ts.getLineno();
1205: pn = expr(false);
1206: pn = nf.createExprStatement(pn, lineno);
1207: break;
1208: }
1209: }
1210:
1211: int ttFlagged = peekFlaggedToken();
1212: switch (ttFlagged & CLEAR_TI_MASK) {
1213: case Token.SEMI:
1214: // Consume ';' as a part of expression
1215: consumeToken();
1216: break;
1217: case Token.ERROR:
1218: case Token.EOF:
1219: case Token.RC:
1220: // Autoinsert ;
1221: break;
1222: default:
1223: if ((ttFlagged & TI_AFTER_EOL) == 0) {
1224: // Report error if no EOL or autoinsert ; otherwise
1225: reportError("msg.no.semi.stmt");
1226: }
1227: break;
1228: }
1229: decompiler.addEOL(Token.SEMI);
1230:
1231: return pn;
1232: }
1233:
1234: /**
1235: * Returns whether or not the bits in the mask have changed to all set.
1236: * @param before bits before change
1237: * @param after bits after change
1238: * @param mask mask for bits
1239: * @return true if all the bits in the mask are set in "after" but not
1240: * "before"
1241: */
1242: private static final boolean nowAllSet(int before, int after,
1243: int mask) {
1244: return ((before & mask) != mask) && ((after & mask) == mask);
1245: }
1246:
1247: private Node returnOrYield(int tt, boolean exprContext)
1248: throws IOException, ParserException {
1249: if (!insideFunction()) {
1250: reportError(tt == Token.RETURN ? "msg.bad.return"
1251: : "msg.bad.yield");
1252: }
1253: consumeToken();
1254: decompiler.addToken(tt);
1255: int lineno = ts.getLineno();
1256:
1257: Node e;
1258: /* This is ugly, but we don't want to require a semicolon. */
1259: switch (peekTokenOrEOL()) {
1260: case Token.SEMI:
1261: case Token.RC:
1262: case Token.EOF:
1263: case Token.EOL:
1264: case Token.ERROR:
1265: case Token.RB:
1266: case Token.RP:
1267: case Token.YIELD:
1268: e = null;
1269: break;
1270: default:
1271: e = expr(false);
1272: break;
1273: }
1274:
1275: int before = endFlags;
1276: Node ret;
1277:
1278: if (tt == Token.RETURN) {
1279: if (e == null) {
1280: endFlags |= Node.END_RETURNS;
1281: } else {
1282: endFlags |= Node.END_RETURNS_VALUE;
1283: hasReturnValue = true;
1284: }
1285: ret = nf.createReturn(e, lineno);
1286:
1287: // see if we need a strict mode warning
1288: if (nowAllSet(before, endFlags, Node.END_RETURNS
1289: | Node.END_RETURNS_VALUE)) {
1290: addStrictWarning("msg.return.inconsistent", "");
1291: }
1292: } else {
1293: endFlags |= Node.END_YIELDS;
1294: ret = nf.createYield(e, lineno);
1295: if (!exprContext)
1296: ret = new Node(Token.EXPR_VOID, ret, lineno);
1297: }
1298:
1299: // see if we are mixing yields and value returns.
1300: if (nowAllSet(before, endFlags, Node.END_YIELDS
1301: | Node.END_RETURNS_VALUE)) {
1302: String name = ((FunctionNode) currentScriptOrFn)
1303: .getFunctionName();
1304: if (name.length() == 0)
1305: addError("msg.anon.generator.returns", "");
1306: else
1307: addError("msg.generator.returns", name);
1308: }
1309:
1310: return ret;
1311: }
1312:
1313: /**
1314: * Parse a 'var' or 'const' statement, or a 'var' init list in a for
1315: * statement.
1316: * @param inFor true if we are currently in the midst of the init
1317: * clause of a for.
1318: * @param inStatement true if called in a statement (as opposed to an
1319: * expression) context
1320: * @param declType A token value: either VAR, CONST, or LET depending on
1321: * context.
1322: * @return The parsed statement
1323: * @throws IOException
1324: * @throws ParserException
1325: */
1326: private Node variables(boolean inFor, int declType)
1327: throws IOException, ParserException {
1328: Node result = nf.createVariables(declType, ts.getLineno());
1329: boolean first = true;
1330: for (;;) {
1331: Node destructuring = null;
1332: String s = null;
1333: int tt = peekToken();
1334: if (tt == Token.LB || tt == Token.LC) {
1335: // Destructuring assignment, e.g., var [a,b] = ...
1336: destructuring = primaryExpr();
1337: } else {
1338: // Simple variable name
1339: mustMatchToken(Token.NAME, "msg.bad.var");
1340: s = ts.getString();
1341:
1342: if (!first)
1343: decompiler.addToken(Token.COMMA);
1344: first = false;
1345:
1346: decompiler.addName(s);
1347: defineSymbol(declType, s);
1348: }
1349:
1350: Node init = null;
1351: if (matchToken(Token.ASSIGN)) {
1352: decompiler.addToken(Token.ASSIGN);
1353: init = assignExpr(inFor);
1354: }
1355:
1356: if (destructuring != null) {
1357: if (init == null) {
1358: if (!inFor)
1359: reportError("msg.destruct.assign.no.init");
1360: nf.addChildToBack(result, destructuring);
1361: } else {
1362: nf.addChildToBack(result, nf
1363: .createDestructuringAssignment(declType,
1364: destructuring, init));
1365: }
1366: } else {
1367: Node name = nf.createName(s);
1368: if (init != null)
1369: nf.addChildToBack(name, init);
1370: nf.addChildToBack(result, name);
1371: }
1372:
1373: if (!matchToken(Token.COMMA))
1374: break;
1375: }
1376: return result;
1377: }
1378:
1379: private Node let(boolean isStatement) throws IOException,
1380: ParserException {
1381: mustMatchToken(Token.LP, "msg.no.paren.after.let");
1382: decompiler.addToken(Token.LP);
1383: Node result = nf.createScopeNode(Token.LET, ts.getLineno());
1384: pushScope(result);
1385: try {
1386: Node vars = variables(false, Token.LET);
1387: nf.addChildToBack(result, vars);
1388: mustMatchToken(Token.RP, "msg.no.paren.let");
1389: decompiler.addToken(Token.RP);
1390: if (isStatement && peekToken() == Token.LC) {
1391: // let statement
1392: consumeToken();
1393: decompiler.addEOL(Token.LC);
1394: nf.addChildToBack(result, statements(null));
1395: mustMatchToken(Token.RC, "msg.no.curly.let");
1396: decompiler.addToken(Token.RC);
1397: } else {
1398: // let expression
1399: result.setType(Token.LETEXPR);
1400: nf.addChildToBack(result, expr(false));
1401: if (isStatement) {
1402: // let expression in statement context
1403: result = nf.createExprStatement(result, ts
1404: .getLineno());
1405: }
1406: }
1407: } finally {
1408: popScope();
1409: }
1410: return result;
1411: }
1412:
1413: void defineSymbol(int declType, String name) {
1414: Node.Scope definingScope = currentScope.getDefiningScope(name);
1415: Node.Scope.Symbol symbol = definingScope != null ? definingScope
1416: .getSymbol(name)
1417: : null;
1418: boolean error = false;
1419: if (symbol != null
1420: && (symbol.declType == Token.CONST || declType == Token.CONST)) {
1421: error = true;
1422: } else {
1423: switch (declType) {
1424: case Token.LET:
1425: if (symbol != null && definingScope == currentScope) {
1426: error = symbol.declType == Token.LET;
1427: }
1428: currentScope.putSymbol(name, new Node.Scope.Symbol(
1429: declType, name));
1430: break;
1431:
1432: case Token.VAR:
1433: case Token.CONST:
1434: case Token.FUNCTION:
1435: if (symbol != null) {
1436: if (symbol.declType == Token.VAR)
1437: addStrictWarning("msg.var.redecl", name);
1438: else if (symbol.declType == Token.LP) {
1439: addStrictWarning("msg.var.hides.arg", name);
1440: }
1441: } else {
1442: currentScriptOrFn.putSymbol(name,
1443: new Node.Scope.Symbol(declType, name));
1444: }
1445: break;
1446:
1447: case Token.LP:
1448: if (symbol != null) {
1449: // must be duplicate parameter. Second parameter hides the
1450: // first, so go ahead and add the second pararameter
1451: addWarning("msg.dup.parms", name);
1452: }
1453: currentScriptOrFn.putSymbol(name,
1454: new Node.Scope.Symbol(declType, name));
1455: break;
1456:
1457: default:
1458: throw Kit.codeBug();
1459: }
1460: }
1461: if (error) {
1462: addError(
1463: symbol.declType == Token.CONST ? "msg.const.redecl"
1464: : symbol.declType == Token.LET ? "msg.let.redecl"
1465: : symbol.declType == Token.VAR ? "msg.var.redecl"
1466: : symbol.declType == Token.FUNCTION ? "msg.fn.redecl"
1467: : "msg.parm.redecl",
1468: name);
1469: }
1470: }
1471:
1472: private Node expr(boolean inForInit) throws IOException,
1473: ParserException {
1474: Node pn = assignExpr(inForInit);
1475: while (matchToken(Token.COMMA)) {
1476: decompiler.addToken(Token.COMMA);
1477: if (compilerEnv.isStrictMode() && !pn.hasSideEffects())
1478: addStrictWarning("msg.no.side.effects", "");
1479: if (peekToken() == Token.YIELD) {
1480: reportError("msg.yield.parenthesized");
1481: }
1482: pn = nf
1483: .createBinary(Token.COMMA, pn,
1484: assignExpr(inForInit));
1485: }
1486: return pn;
1487: }
1488:
1489: private Node assignExpr(boolean inForInit) throws IOException,
1490: ParserException {
1491: int tt = peekToken();
1492: if (tt == Token.YIELD) {
1493: consumeToken();
1494: return returnOrYield(tt, true);
1495: }
1496: Node pn = condExpr(inForInit);
1497:
1498: tt = peekToken();
1499: if (Token.FIRST_ASSIGN <= tt && tt <= Token.LAST_ASSIGN) {
1500: consumeToken();
1501: decompiler.addToken(tt);
1502: pn = nf.createAssignment(tt, pn, assignExpr(inForInit));
1503: }
1504:
1505: return pn;
1506: }
1507:
1508: private Node condExpr(boolean inForInit) throws IOException,
1509: ParserException {
1510: Node pn = orExpr(inForInit);
1511:
1512: if (matchToken(Token.HOOK)) {
1513: decompiler.addToken(Token.HOOK);
1514: Node ifTrue = assignExpr(false);
1515: mustMatchToken(Token.COLON, "msg.no.colon.cond");
1516: decompiler.addToken(Token.COLON);
1517: Node ifFalse = assignExpr(inForInit);
1518: return nf.createCondExpr(pn, ifTrue, ifFalse);
1519: }
1520:
1521: return pn;
1522: }
1523:
1524: private Node orExpr(boolean inForInit) throws IOException,
1525: ParserException {
1526: Node pn = andExpr(inForInit);
1527: if (matchToken(Token.OR)) {
1528: decompiler.addToken(Token.OR);
1529: pn = nf.createBinary(Token.OR, pn, orExpr(inForInit));
1530: }
1531:
1532: return pn;
1533: }
1534:
1535: private Node andExpr(boolean inForInit) throws IOException,
1536: ParserException {
1537: Node pn = bitOrExpr(inForInit);
1538: if (matchToken(Token.AND)) {
1539: decompiler.addToken(Token.AND);
1540: pn = nf.createBinary(Token.AND, pn, andExpr(inForInit));
1541: }
1542:
1543: return pn;
1544: }
1545:
1546: private Node bitOrExpr(boolean inForInit) throws IOException,
1547: ParserException {
1548: Node pn = bitXorExpr(inForInit);
1549: while (matchToken(Token.BITOR)) {
1550: decompiler.addToken(Token.BITOR);
1551: pn = nf
1552: .createBinary(Token.BITOR, pn,
1553: bitXorExpr(inForInit));
1554: }
1555: return pn;
1556: }
1557:
1558: private Node bitXorExpr(boolean inForInit) throws IOException,
1559: ParserException {
1560: Node pn = bitAndExpr(inForInit);
1561: while (matchToken(Token.BITXOR)) {
1562: decompiler.addToken(Token.BITXOR);
1563: pn = nf.createBinary(Token.BITXOR, pn,
1564: bitAndExpr(inForInit));
1565: }
1566: return pn;
1567: }
1568:
1569: private Node bitAndExpr(boolean inForInit) throws IOException,
1570: ParserException {
1571: Node pn = eqExpr(inForInit);
1572: while (matchToken(Token.BITAND)) {
1573: decompiler.addToken(Token.BITAND);
1574: pn = nf.createBinary(Token.BITAND, pn, eqExpr(inForInit));
1575: }
1576: return pn;
1577: }
1578:
1579: private Node eqExpr(boolean inForInit) throws IOException,
1580: ParserException {
1581: Node pn = relExpr(inForInit);
1582: for (;;) {
1583: int tt = peekToken();
1584: switch (tt) {
1585: case Token.EQ:
1586: case Token.NE:
1587: case Token.SHEQ:
1588: case Token.SHNE:
1589: consumeToken();
1590: int decompilerToken = tt;
1591: int parseToken = tt;
1592: if (compilerEnv.getLanguageVersion() == Context.VERSION_1_2) {
1593: // JavaScript 1.2 uses shallow equality for == and != .
1594: // In addition, convert === and !== for decompiler into
1595: // == and != since the decompiler is supposed to show
1596: // canonical source and in 1.2 ===, !== are allowed
1597: // only as an alias to ==, !=.
1598: switch (tt) {
1599: case Token.EQ:
1600: parseToken = Token.SHEQ;
1601: break;
1602: case Token.NE:
1603: parseToken = Token.SHNE;
1604: break;
1605: case Token.SHEQ:
1606: decompilerToken = Token.EQ;
1607: break;
1608: case Token.SHNE:
1609: decompilerToken = Token.NE;
1610: break;
1611: }
1612: }
1613: decompiler.addToken(decompilerToken);
1614: pn = nf
1615: .createBinary(parseToken, pn,
1616: relExpr(inForInit));
1617: continue;
1618: }
1619: break;
1620: }
1621: return pn;
1622: }
1623:
1624: private Node relExpr(boolean inForInit) throws IOException,
1625: ParserException {
1626: Node pn = shiftExpr();
1627: for (;;) {
1628: int tt = peekToken();
1629: switch (tt) {
1630: case Token.IN:
1631: if (inForInit)
1632: break;
1633: // fall through
1634: case Token.INSTANCEOF:
1635: case Token.LE:
1636: case Token.LT:
1637: case Token.GE:
1638: case Token.GT:
1639: consumeToken();
1640: decompiler.addToken(tt);
1641: pn = nf.createBinary(tt, pn, shiftExpr());
1642: continue;
1643: }
1644: break;
1645: }
1646: return pn;
1647: }
1648:
1649: private Node shiftExpr() throws IOException, ParserException {
1650: Node pn = addExpr();
1651: for (;;) {
1652: int tt = peekToken();
1653: switch (tt) {
1654: case Token.LSH:
1655: case Token.URSH:
1656: case Token.RSH:
1657: consumeToken();
1658: decompiler.addToken(tt);
1659: pn = nf.createBinary(tt, pn, addExpr());
1660: continue;
1661: }
1662: break;
1663: }
1664: return pn;
1665: }
1666:
1667: private Node addExpr() throws IOException, ParserException {
1668: Node pn = mulExpr();
1669: for (;;) {
1670: int tt = peekToken();
1671: if (tt == Token.ADD || tt == Token.SUB) {
1672: consumeToken();
1673: decompiler.addToken(tt);
1674: // flushNewLines
1675: pn = nf.createBinary(tt, pn, mulExpr());
1676: continue;
1677: }
1678: break;
1679: }
1680:
1681: return pn;
1682: }
1683:
1684: private Node mulExpr() throws IOException, ParserException {
1685: Node pn = unaryExpr();
1686: for (;;) {
1687: int tt = peekToken();
1688: switch (tt) {
1689: case Token.MUL:
1690: case Token.DIV:
1691: case Token.MOD:
1692: consumeToken();
1693: decompiler.addToken(tt);
1694: pn = nf.createBinary(tt, pn, unaryExpr());
1695: continue;
1696: }
1697: break;
1698: }
1699:
1700: return pn;
1701: }
1702:
1703: private Node unaryExpr() throws IOException, ParserException {
1704: int tt;
1705:
1706: tt = peekToken();
1707:
1708: switch (tt) {
1709: case Token.VOID:
1710: case Token.NOT:
1711: case Token.BITNOT:
1712: case Token.TYPEOF:
1713: consumeToken();
1714: decompiler.addToken(tt);
1715: return nf.createUnary(tt, unaryExpr());
1716:
1717: case Token.ADD:
1718: consumeToken();
1719: // Convert to special POS token in decompiler and parse tree
1720: decompiler.addToken(Token.POS);
1721: return nf.createUnary(Token.POS, unaryExpr());
1722:
1723: case Token.SUB:
1724: consumeToken();
1725: // Convert to special NEG token in decompiler and parse tree
1726: decompiler.addToken(Token.NEG);
1727: return nf.createUnary(Token.NEG, unaryExpr());
1728:
1729: case Token.INC:
1730: case Token.DEC:
1731: consumeToken();
1732: decompiler.addToken(tt);
1733: return nf.createIncDec(tt, false, memberExpr(true));
1734:
1735: case Token.DELPROP:
1736: consumeToken();
1737: decompiler.addToken(Token.DELPROP);
1738: return nf.createUnary(Token.DELPROP, unaryExpr());
1739:
1740: case Token.ERROR:
1741: consumeToken();
1742: break;
1743:
1744: // XML stream encountered in expression.
1745: case Token.LT:
1746: if (compilerEnv.isXmlAvailable()) {
1747: consumeToken();
1748: Node pn = xmlInitializer();
1749: return memberExprTail(true, pn);
1750: }
1751: // Fall thru to the default handling of RELOP
1752:
1753: default:
1754: Node pn = memberExpr(true);
1755:
1756: // Don't look across a newline boundary for a postfix incop.
1757: tt = peekTokenOrEOL();
1758: if (tt == Token.INC || tt == Token.DEC) {
1759: consumeToken();
1760: decompiler.addToken(tt);
1761: return nf.createIncDec(tt, true, pn);
1762: }
1763: return pn;
1764: }
1765: return nf.createName("error"); // Only reached on error.Try to continue.
1766:
1767: }
1768:
1769: private Node xmlInitializer() throws IOException {
1770: int tt = ts.getFirstXMLToken();
1771: if (tt != Token.XML && tt != Token.XMLEND) {
1772: reportError("msg.syntax");
1773: return null;
1774: }
1775:
1776: /* Make a NEW node to append to. */
1777: Node pnXML = nf.createLeaf(Token.NEW);
1778:
1779: String xml = ts.getString();
1780: boolean fAnonymous = xml.trim().startsWith("<>");
1781:
1782: Node pn = nf.createName(fAnonymous ? "XMLList" : "XML");
1783: nf.addChildToBack(pnXML, pn);
1784:
1785: pn = null;
1786: Node expr;
1787: for (;; tt = ts.getNextXMLToken()) {
1788: switch (tt) {
1789: case Token.XML:
1790: xml = ts.getString();
1791: decompiler.addName(xml);
1792: mustMatchToken(Token.LC, "msg.syntax");
1793: decompiler.addToken(Token.LC);
1794: expr = (peekToken() == Token.RC) ? nf.createString("")
1795: : expr(false);
1796: mustMatchToken(Token.RC, "msg.syntax");
1797: decompiler.addToken(Token.RC);
1798: if (pn == null) {
1799: pn = nf.createString(xml);
1800: } else {
1801: pn = nf.createBinary(Token.ADD, pn, nf
1802: .createString(xml));
1803: }
1804: if (ts.isXMLAttribute()) {
1805: /* Need to put the result in double quotes */
1806: expr = nf.createUnary(Token.ESCXMLATTR, expr);
1807: Node prepend = nf.createBinary(Token.ADD, nf
1808: .createString("\""), expr);
1809: expr = nf.createBinary(Token.ADD, prepend, nf
1810: .createString("\""));
1811: } else {
1812: expr = nf.createUnary(Token.ESCXMLTEXT, expr);
1813: }
1814: pn = nf.createBinary(Token.ADD, pn, expr);
1815: break;
1816: case Token.XMLEND:
1817: xml = ts.getString();
1818: decompiler.addName(xml);
1819: if (pn == null) {
1820: pn = nf.createString(xml);
1821: } else {
1822: pn = nf.createBinary(Token.ADD, pn, nf
1823: .createString(xml));
1824: }
1825:
1826: nf.addChildToBack(pnXML, pn);
1827: return pnXML;
1828: default:
1829: reportError("msg.syntax");
1830: return null;
1831: }
1832: }
1833: }
1834:
1835: private void argumentList(Node listNode) throws IOException,
1836: ParserException {
1837: boolean matched;
1838: matched = matchToken(Token.RP);
1839: if (!matched) {
1840: boolean first = true;
1841: do {
1842: if (!first)
1843: decompiler.addToken(Token.COMMA);
1844: first = false;
1845: if (peekToken() == Token.YIELD) {
1846: reportError("msg.yield.parenthesized");
1847: }
1848: nf.addChildToBack(listNode, assignExpr(false));
1849: } while (matchToken(Token.COMMA));
1850:
1851: mustMatchToken(Token.RP, "msg.no.paren.arg");
1852: }
1853: decompiler.addToken(Token.RP);
1854: }
1855:
1856: private Node memberExpr(boolean allowCallSyntax)
1857: throws IOException, ParserException {
1858: int tt;
1859:
1860: Node pn;
1861:
1862: /* Check for new expressions. */
1863: tt = peekToken();
1864: if (tt == Token.NEW) {
1865: /* Eat the NEW token. */
1866: consumeToken();
1867: decompiler.addToken(Token.NEW);
1868:
1869: /* Make a NEW node to append to. */
1870: pn = nf.createCallOrNew(Token.NEW, memberExpr(false));
1871:
1872: if (matchToken(Token.LP)) {
1873: decompiler.addToken(Token.LP);
1874: /* Add the arguments to pn, if any are supplied. */
1875: argumentList(pn);
1876: }
1877:
1878: /* XXX there's a check in the C source against
1879: * "too many constructor arguments" - how many
1880: * do we claim to support?
1881: */
1882:
1883: /* Experimental syntax: allow an object literal to follow a new expression,
1884: * which will mean a kind of anonymous class built with the JavaAdapter.
1885: * the object literal will be passed as an additional argument to the constructor.
1886: */
1887: tt = peekToken();
1888: if (tt == Token.LC) {
1889: nf.addChildToBack(pn, primaryExpr());
1890: }
1891: } else {
1892: pn = primaryExpr();
1893: }
1894:
1895: return memberExprTail(allowCallSyntax, pn);
1896: }
1897:
1898: private Node memberExprTail(boolean allowCallSyntax, Node pn)
1899: throws IOException, ParserException {
1900: tailLoop: for (;;) {
1901: int tt = peekToken();
1902: switch (tt) {
1903:
1904: case Token.DOT:
1905: case Token.DOTDOT: {
1906: int memberTypeFlags;
1907: String s;
1908:
1909: consumeToken();
1910: decompiler.addToken(tt);
1911: memberTypeFlags = 0;
1912: if (tt == Token.DOTDOT) {
1913: mustHaveXML();
1914: memberTypeFlags = Node.DESCENDANTS_FLAG;
1915: }
1916: if (!compilerEnv.isXmlAvailable()) {
1917: mustMatchToken(Token.NAME, "msg.no.name.after.dot");
1918: s = ts.getString();
1919: decompiler.addName(s);
1920: pn = nf.createPropertyGet(pn, null, s,
1921: memberTypeFlags);
1922: break;
1923: }
1924:
1925: tt = nextToken();
1926: switch (tt) {
1927:
1928: // needed for generator.throw();
1929: case Token.THROW:
1930: decompiler.addName("throw");
1931: pn = propertyName(pn, "throw", memberTypeFlags);
1932: break;
1933:
1934: // handles: name, ns::name, ns::*, ns::[expr]
1935: case Token.NAME:
1936: s = ts.getString();
1937: decompiler.addName(s);
1938: pn = propertyName(pn, s, memberTypeFlags);
1939: break;
1940:
1941: // handles: *, *::name, *::*, *::[expr]
1942: case Token.MUL:
1943: decompiler.addName("*");
1944: pn = propertyName(pn, "*", memberTypeFlags);
1945: break;
1946:
1947: // handles: '@attr', '@ns::attr', '@ns::*', '@ns::*',
1948: // '@::attr', '@::*', '@*', '@*::attr', '@*::*'
1949: case Token.XMLATTR:
1950: decompiler.addToken(Token.XMLATTR);
1951: pn = attributeAccess(pn, memberTypeFlags);
1952: break;
1953:
1954: default:
1955: reportError("msg.no.name.after.dot");
1956: }
1957: }
1958: break;
1959:
1960: case Token.DOTQUERY:
1961: consumeToken();
1962: mustHaveXML();
1963: decompiler.addToken(Token.DOTQUERY);
1964: pn = nf.createDotQuery(pn, expr(false), ts.getLineno());
1965: mustMatchToken(Token.RP, "msg.no.paren");
1966: decompiler.addToken(Token.RP);
1967: break;
1968:
1969: case Token.LB:
1970: consumeToken();
1971: decompiler.addToken(Token.LB);
1972: pn = nf.createElementGet(pn, null, expr(false), 0);
1973: mustMatchToken(Token.RB, "msg.no.bracket.index");
1974: decompiler.addToken(Token.RB);
1975: break;
1976:
1977: case Token.LP:
1978: if (!allowCallSyntax) {
1979: break tailLoop;
1980: }
1981: consumeToken();
1982: decompiler.addToken(Token.LP);
1983: pn = nf.createCallOrNew(Token.CALL, pn);
1984: /* Add the arguments to pn, if any are supplied. */
1985: argumentList(pn);
1986: break;
1987:
1988: default:
1989: break tailLoop;
1990: }
1991: }
1992: return pn;
1993: }
1994:
1995: /*
1996: * Xml attribute expression:
1997: * '@attr', '@ns::attr', '@ns::*', '@ns::*', '@*', '@*::attr', '@*::*'
1998: */
1999: private Node attributeAccess(Node pn, int memberTypeFlags)
2000: throws IOException {
2001: memberTypeFlags |= Node.ATTRIBUTE_FLAG;
2002: int tt = nextToken();
2003:
2004: switch (tt) {
2005: // handles: @name, @ns::name, @ns::*, @ns::[expr]
2006: case Token.NAME: {
2007: String s = ts.getString();
2008: decompiler.addName(s);
2009: pn = propertyName(pn, s, memberTypeFlags);
2010: }
2011: break;
2012:
2013: // handles: @*, @*::name, @*::*, @*::[expr]
2014: case Token.MUL:
2015: decompiler.addName("*");
2016: pn = propertyName(pn, "*", memberTypeFlags);
2017: break;
2018:
2019: // handles @[expr]
2020: case Token.LB:
2021: decompiler.addToken(Token.LB);
2022: pn = nf.createElementGet(pn, null, expr(false),
2023: memberTypeFlags);
2024: mustMatchToken(Token.RB, "msg.no.bracket.index");
2025: decompiler.addToken(Token.RB);
2026: break;
2027:
2028: default:
2029: reportError("msg.no.name.after.xmlAttr");
2030: pn = nf.createPropertyGet(pn, null, "?", memberTypeFlags);
2031: break;
2032: }
2033:
2034: return pn;
2035: }
2036:
2037: /**
2038: * Check if :: follows name in which case it becomes qualified name
2039: */
2040: private Node propertyName(Node pn, String name, int memberTypeFlags)
2041: throws IOException, ParserException {
2042: String namespace = null;
2043: if (matchToken(Token.COLONCOLON)) {
2044: decompiler.addToken(Token.COLONCOLON);
2045: namespace = name;
2046:
2047: int tt = nextToken();
2048: switch (tt) {
2049: // handles name::name
2050: case Token.NAME:
2051: name = ts.getString();
2052: decompiler.addName(name);
2053: break;
2054:
2055: // handles name::*
2056: case Token.MUL:
2057: decompiler.addName("*");
2058: name = "*";
2059: break;
2060:
2061: // handles name::[expr]
2062: case Token.LB:
2063: decompiler.addToken(Token.LB);
2064: pn = nf.createElementGet(pn, namespace, expr(false),
2065: memberTypeFlags);
2066: mustMatchToken(Token.RB, "msg.no.bracket.index");
2067: decompiler.addToken(Token.RB);
2068: return pn;
2069:
2070: default:
2071: reportError("msg.no.name.after.coloncolon");
2072: name = "?";
2073: }
2074: }
2075:
2076: pn = nf.createPropertyGet(pn, namespace, name, memberTypeFlags);
2077: return pn;
2078: }
2079:
2080: private Node arrayComprehension(String arrayName, Node expr)
2081: throws IOException, ParserException {
2082: if (nextToken() != Token.FOR)
2083: throw Kit.codeBug(); // shouldn't be here if next token isn't 'for'
2084: decompiler.addName(" "); // space after array literal expr
2085: decompiler.addToken(Token.FOR);
2086: boolean isForEach = false;
2087: if (matchToken(Token.NAME)) {
2088: decompiler.addName(ts.getString());
2089: if (ts.getString().equals("each")) {
2090: isForEach = true;
2091: } else {
2092: reportError("msg.no.paren.for");
2093: }
2094: }
2095: mustMatchToken(Token.LP, "msg.no.paren.for");
2096: decompiler.addToken(Token.LP);
2097: String name;
2098: int tt = peekToken();
2099: if (tt == Token.LB || tt == Token.LC) {
2100: // handle destructuring assignment
2101: name = currentScriptOrFn.getNextTempName();
2102: defineSymbol(Token.LP, name);
2103: expr = nf.createBinary(Token.COMMA, nf.createAssignment(
2104: Token.ASSIGN, primaryExpr(), nf.createName(name)),
2105: expr);
2106: } else if (tt == Token.NAME) {
2107: consumeToken();
2108: name = ts.getString();
2109: decompiler.addName(name);
2110: } else {
2111: reportError("msg.bad.var");
2112: return nf.createNumber(0);
2113: }
2114:
2115: Node init = nf.createName(name);
2116: // Define as a let since we want the scope of the variable to
2117: // be restricted to the array comprehension
2118: defineSymbol(Token.LET, name);
2119:
2120: mustMatchToken(Token.IN, "msg.in.after.for.name");
2121: decompiler.addToken(Token.IN);
2122: Node iterator = expr(false);
2123: mustMatchToken(Token.RP, "msg.no.paren.for.ctrl");
2124: decompiler.addToken(Token.RP);
2125:
2126: Node body;
2127: tt = peekToken();
2128: if (tt == Token.FOR) {
2129: body = arrayComprehension(arrayName, expr);
2130: } else {
2131: Node call = nf.createCallOrNew(Token.CALL, nf
2132: .createPropertyGet(nf.createName(arrayName), null,
2133: "push", 0));
2134: call.addChildToBack(expr);
2135: body = new Node(Token.EXPR_VOID, call, ts.getLineno());
2136: if (tt == Token.IF) {
2137: consumeToken();
2138: decompiler.addToken(Token.IF);
2139: int lineno = ts.getLineno();
2140: Node cond = condition();
2141: body = nf.createIf(cond, body, null, lineno);
2142: }
2143: mustMatchToken(Token.RB, "msg.no.bracket.arg");
2144: decompiler.addToken(Token.RB);
2145: }
2146:
2147: Node loop = enterLoop(null, true);
2148: try {
2149: return nf.createForIn(Token.LET, loop, init, iterator,
2150: body, isForEach);
2151: } finally {
2152: exitLoop(false);
2153: }
2154: }
2155:
2156: private Node primaryExpr() throws IOException, ParserException {
2157: Node pn;
2158:
2159: int ttFlagged = nextFlaggedToken();
2160: int tt = ttFlagged & CLEAR_TI_MASK;
2161:
2162: switch (tt) {
2163:
2164: case Token.FUNCTION:
2165: return function(FunctionNode.FUNCTION_EXPRESSION);
2166:
2167: case Token.LB: {
2168: ObjArray elems = new ObjArray();
2169: int skipCount = 0;
2170: int destructuringLen = 0;
2171: decompiler.addToken(Token.LB);
2172: boolean after_lb_or_comma = true;
2173: for (;;) {
2174: tt = peekToken();
2175:
2176: if (tt == Token.COMMA) {
2177: consumeToken();
2178: decompiler.addToken(Token.COMMA);
2179: if (!after_lb_or_comma) {
2180: after_lb_or_comma = true;
2181: } else {
2182: elems.add(null);
2183: ++skipCount;
2184: }
2185: } else if (tt == Token.RB) {
2186: consumeToken();
2187: decompiler.addToken(Token.RB);
2188: // for ([a,] in obj) is legal, but for ([a] in obj) is
2189: // not since we have both key and value supplied. The
2190: // trick is that [a,] and [a] are equivalent in other
2191: // array literal contexts. So we calculate a special
2192: // length value just for destructuring assignment.
2193: destructuringLen = elems.size()
2194: + (after_lb_or_comma ? 1 : 0);
2195: break;
2196: } else if (skipCount == 0 && elems.size() == 1
2197: && tt == Token.FOR) {
2198: Node scopeNode = nf.createScopeNode(
2199: Token.ARRAYCOMP, ts.getLineno());
2200: String tempName = currentScriptOrFn
2201: .getNextTempName();
2202: pushScope(scopeNode);
2203: try {
2204: defineSymbol(Token.LET, tempName);
2205: Node expr = (Node) elems.get(0);
2206: Node block = nf.createBlock(ts.getLineno());
2207: Node init = new Node(Token.EXPR_VOID, nf
2208: .createAssignment(Token.ASSIGN, nf
2209: .createName(tempName), nf
2210: .createCallOrNew(Token.NEW, nf
2211: .createName("Array"))),
2212: ts.getLineno());
2213: block.addChildToBack(init);
2214: block.addChildToBack(arrayComprehension(
2215: tempName, expr));
2216: scopeNode.addChildToBack(block);
2217: scopeNode.addChildToBack(nf
2218: .createName(tempName));
2219: return scopeNode;
2220: } finally {
2221: popScope();
2222: }
2223: } else {
2224: if (!after_lb_or_comma) {
2225: reportError("msg.no.bracket.arg");
2226: }
2227: elems.add(assignExpr(false));
2228: after_lb_or_comma = false;
2229: }
2230: }
2231: return nf.createArrayLiteral(elems, skipCount,
2232: destructuringLen);
2233: }
2234:
2235: case Token.LC: {
2236: ObjArray elems = new ObjArray();
2237: decompiler.addToken(Token.LC);
2238: if (!matchToken(Token.RC)) {
2239:
2240: boolean first = true;
2241: commaloop: do {
2242: Object property;
2243:
2244: if (!first)
2245: decompiler.addToken(Token.COMMA);
2246: else
2247: first = false;
2248:
2249: tt = peekToken();
2250: switch (tt) {
2251: case Token.NAME:
2252: case Token.STRING:
2253: consumeToken();
2254: // map NAMEs to STRINGs in object literal context
2255: // but tell the decompiler the proper type
2256: String s = ts.getString();
2257: if (tt == Token.NAME) {
2258: if (s.equals("get")
2259: && peekToken() == Token.NAME) {
2260: decompiler.addToken(Token.GET);
2261: consumeToken();
2262: s = ts.getString();
2263: decompiler.addName(s);
2264: property = ScriptRuntime
2265: .getIndexObject(s);
2266: if (!getterSetterProperty(elems,
2267: property, true))
2268: break commaloop;
2269: break;
2270: } else if (s.equals("set")
2271: && peekToken() == Token.NAME) {
2272: decompiler.addToken(Token.SET);
2273: consumeToken();
2274: s = ts.getString();
2275: decompiler.addName(s);
2276: property = ScriptRuntime
2277: .getIndexObject(s);
2278: if (!getterSetterProperty(elems,
2279: property, false))
2280: break commaloop;
2281: break;
2282: }
2283: decompiler.addName(s);
2284: } else {
2285: decompiler.addString(s);
2286: }
2287: property = ScriptRuntime.getIndexObject(s);
2288: plainProperty(elems, property);
2289: break;
2290:
2291: case Token.NUMBER:
2292: consumeToken();
2293: double n = ts.getNumber();
2294: decompiler.addNumber(n);
2295: property = ScriptRuntime.getIndexObject(n);
2296: plainProperty(elems, property);
2297: break;
2298:
2299: case Token.RC:
2300: // trailing comma is OK.
2301: break commaloop;
2302: default:
2303: reportError("msg.bad.prop");
2304: break commaloop;
2305: }
2306: } while (matchToken(Token.COMMA));
2307:
2308: mustMatchToken(Token.RC, "msg.no.brace.prop");
2309: }
2310: decompiler.addToken(Token.RC);
2311: return nf.createObjectLiteral(elems);
2312: }
2313:
2314: case Token.LET:
2315: decompiler.addToken(Token.LET);
2316: return let(false);
2317:
2318: case Token.LP:
2319:
2320: /* Brendan's IR-jsparse.c makes a new node tagged with
2321: * TOK_LP here... I'm not sure I understand why. Isn't
2322: * the grouping already implicit in the structure of the
2323: * parse tree? also TOK_LP is already overloaded (I
2324: * think) in the C IR as 'function call.' */
2325: decompiler.addToken(Token.LP);
2326: pn = expr(false);
2327: pn.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
2328: decompiler.addToken(Token.RP);
2329: mustMatchToken(Token.RP, "msg.no.paren");
2330: return pn;
2331:
2332: case Token.XMLATTR:
2333: mustHaveXML();
2334: decompiler.addToken(Token.XMLATTR);
2335: pn = attributeAccess(null, 0);
2336: return pn;
2337:
2338: case Token.NAME: {
2339: String name = ts.getString();
2340: if ((ttFlagged & TI_CHECK_LABEL) != 0) {
2341: if (peekToken() == Token.COLON) {
2342: // Do not consume colon, it is used as unwind indicator
2343: // to return to statementHelper.
2344: // XXX Better way?
2345: return nf.createLabel(ts.getLineno());
2346: }
2347: }
2348:
2349: decompiler.addName(name);
2350: if (compilerEnv.isXmlAvailable()) {
2351: pn = propertyName(null, name, 0);
2352: } else {
2353: pn = nf.createName(name);
2354: }
2355: return pn;
2356: }
2357:
2358: case Token.NUMBER: {
2359: double n = ts.getNumber();
2360: decompiler.addNumber(n);
2361: return nf.createNumber(n);
2362: }
2363:
2364: case Token.STRING: {
2365: String s = ts.getString();
2366: decompiler.addString(s);
2367: return nf.createString(s);
2368: }
2369:
2370: case Token.DIV:
2371: case Token.ASSIGN_DIV: {
2372: // Got / or /= which should be treated as regexp in fact
2373: ts.readRegExp(tt);
2374: String flags = ts.regExpFlags;
2375: ts.regExpFlags = null;
2376: String re = ts.getString();
2377: decompiler.addRegexp(re, flags);
2378: int index = currentScriptOrFn.addRegexp(re, flags);
2379: return nf.createRegExp(index);
2380: }
2381:
2382: case Token.NULL:
2383: case Token.THIS:
2384: case Token.FALSE:
2385: case Token.TRUE:
2386: decompiler.addToken(tt);
2387: return nf.createLeaf(tt);
2388:
2389: case Token.RESERVED:
2390: reportError("msg.reserved.id");
2391: break;
2392:
2393: case Token.ERROR:
2394: /* the scanner or one of its subroutines reported the error. */
2395: break;
2396:
2397: case Token.EOF:
2398: reportError("msg.unexpected.eof");
2399: break;
2400:
2401: default:
2402: reportError("msg.syntax");
2403: break;
2404: }
2405: return null; // should never reach here
2406: }
2407:
2408: private void plainProperty(ObjArray elems, Object property)
2409: throws IOException {
2410: mustMatchToken(Token.COLON, "msg.no.colon.prop");
2411:
2412: // OBJLIT is used as ':' in object literal for
2413: // decompilation to solve spacing ambiguity.
2414: decompiler.addToken(Token.OBJECTLIT);
2415: elems.add(property);
2416: elems.add(assignExpr(false));
2417: }
2418:
2419: private boolean getterSetterProperty(ObjArray elems,
2420: Object property, boolean isGetter) throws IOException {
2421: Node f = function(FunctionNode.FUNCTION_EXPRESSION);
2422: if (f.getType() != Token.FUNCTION) {
2423: reportError("msg.bad.prop");
2424: return false;
2425: }
2426: int fnIndex = f.getExistingIntProp(Node.FUNCTION_PROP);
2427: FunctionNode fn = currentScriptOrFn.getFunctionNode(fnIndex);
2428: if (fn.getFunctionName().length() != 0) {
2429: reportError("msg.bad.prop");
2430: return false;
2431: }
2432: elems.add(property);
2433: if (isGetter) {
2434: elems.add(nf.createUnary(Token.GET, f));
2435: } else {
2436: elems.add(nf.createUnary(Token.SET, f));
2437: }
2438: return true;
2439: }
2440: }
|