0001: /*
0002: * Copyright 2001 Sun Microsystems, Inc. All rights reserved.
0003: * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to license terms.
0004: */
0005: package com.sun.portal.rewriter.engines.js.parser;
0006:
0007: import com.sun.portal.rewriter.Translator;
0008: import com.sun.portal.rewriter.engines.PageContent;
0009: import com.sun.portal.rewriter.engines.js.JSRewriter;
0010: import com.sun.portal.rewriter.rom.RuleSet;
0011: import com.sun.portal.rewriter.rom.RuleSetHelper;
0012: import com.sun.portal.rewriter.rom.js.Function;
0013: import com.sun.portal.rewriter.rom.js.FunctionRule;
0014: import com.sun.portal.rewriter.rom.js.JSData;
0015: import com.sun.portal.rewriter.rom.js.Variable;
0016: import com.sun.portal.rewriter.rom.js.VariableRule;
0017: import com.sun.portal.rewriter.util.ConfigManager;
0018: import com.sun.portal.rewriter.util.Debug;
0019: import com.sun.portal.rewriter.util.StringHelper;
0020:
0021: import java.io.IOException;
0022:
0023: /**
0024: * This class implements the JavaScript parser.
0025: *
0026: * It is based on the C sourceURI files jsparse.c and jsparse.h
0027: * in the jsref package.
0028: */
0029: public final class JSParser {
0030: public static final String PROPERTY_IS_REWRITE_VARS_IN_DHTML = "IS_REWRITE_VARS_IN_DHTML";
0031:
0032: private static final boolean REWRITE_VARS_IN_DHTML = ConfigManager
0033: .getBoolean(PROPERTY_IS_REWRITE_VARS_IN_DHTML, true);
0034:
0035: private final Function functionMatche;
0036: private final Variable variableMatche;
0037:
0038: private final PageContent pageContent;
0039: private final RuleSet ruleSet;
0040: private final Translator translator;
0041:
0042: private final StringBuffer resultBuffer;
0043: private final LineBuffer lineBuffer;
0044: private final TokenStream tokenStream;
0045: private final JSRewriter jsRewriter;
0046:
0047: private int lastExprEndLine; // hack to handle function expr termination.
0048: private boolean ok; // did the parse encounter an error?
0049:
0050: public JSParser(final PageContent aPageContent,
0051: final RuleSet aRuleSet, final Translator aTranslator,
0052: final JSRewriter aJSRewriter) {
0053: pageContent = aPageContent;
0054: ruleSet = aRuleSet;
0055: translator = aTranslator;
0056: jsRewriter = aJSRewriter;
0057:
0058: resultBuffer = pageContent.getResultBuffer();
0059: lineBuffer = new LineBuffer(aPageContent, translator
0060: .getPageSpec().getInputString());
0061: tokenStream = new TokenStream(lineBuffer);
0062: String source = aTranslator.getPageSpec().getPageURI()
0063: .getFullFileURI();//BugNo:4898369
0064: functionMatche = new Function("xxyyzz", null, null, source);
0065: variableMatche = new Variable("xxyyzz", null, source);
0066: }//constructor
0067:
0068: private void mustMatchToken(final int toMatch,
0069: final String messageId) throws IOException,
0070: JavaScriptException {
0071: final int tt = tokenStream.getToken();
0072: if (tt != toMatch) {
0073: reportError(messageId);
0074: tokenStream.ungetToken(tt); // In case the parser decides to continue
0075: }
0076: }//mustMatchToken()
0077:
0078: private void reportError(final String messageId)
0079: throws JavaScriptException {
0080: String message = messageId;
0081:
0082: if (this .ok) {
0083: this .ok = false;
0084:
0085: // Only report the error if the TokenStream hasn't had a chance to.
0086: if ((tokenStream.flags & TokenStream.TSF_ERROR) == 0) {
0087: message = getMessage(messageId, null);
0088: }
0089: }
0090:
0091: /*
0092: * Throw an exception to unwind the recursive descent parse.
0093: * We use JavaScriptException here even though it is really
0094: * a different use of the exception than it is usually used
0095: * for.
0096: */
0097: throw new JavaScriptException(message, lineBuffer);
0098: }//reportError()
0099:
0100: /*
0101: * Build a parse tree from the given TokenStream. Creates a tree
0102: * by making calls to the NodeFactory object that was used to
0103: * construct the Parser.
0104: *
0105: * @param ts the TokenStream to parse
0106: *
0107: * @return an Object representing the parsed
0108: * program. If the parse fails, null will be returned. (The
0109: * parse failure will result in a call to the current Context's
0110: * ErrorReporter.)
0111: */
0112: public void parse() {
0113: try {
0114: this .ok = true;
0115: int tt; // last token from getToken();
0116: /*
0117: * so we have something to add nodes to until
0118: * we've collected all the aContent
0119: */
0120: while (this .ok) {
0121: tokenStream.flags |= TokenStream.TSF_REGEXP;
0122: tt = tokenStream.getToken();
0123:
0124: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0125: if (tt <= TokenStream.EOF) {
0126: break;
0127: }
0128:
0129: if (tt == TokenStream.FUNCTION) {
0130: try {
0131: function();
0132: /* function doesn't add its own final EOL,
0133: * because it gets SEMI + EOL from Statement when it's
0134: * a nested function; so we need to explicitly add an
0135: * EOL here.
0136: */
0137: wellTerminated(TokenStream.FUNCTION);
0138: } catch (JavaScriptException e) {
0139: this .ok = false;
0140: recordWarning(e);
0141: }
0142: } else {
0143: tokenStream.ungetToken(tt);
0144: statement();
0145: }
0146: }
0147: } catch (JavaScriptException je) {
0148: recordWarning(je);
0149: } catch (IOException e) {
0150: pageContent.enableExceptionFlag();
0151: //Debug.error( "Bug in JSParser", e );
0152: Debug.error("PSRW_CSPR_0019", e);
0153: }
0154:
0155: //read reset of the content
0156: try {
0157: lineBuffer.readRemaining();
0158: } catch (Exception e) {
0159: pageContent.enableExceptionFlag();
0160: }
0161: }//parse()
0162:
0163: /*
0164: * The C version of this function takes an argument list,
0165: * which doesn't seem to be needed for tree generation...
0166: * it'd only be useful for checking argument hiding, which
0167: * I'm not doing anyway...
0168: */
0169: private void parseFunctionBody() throws IOException,
0170: JavaScriptException {
0171: final int oldflags = tokenStream.flags;
0172: tokenStream.flags &= ~(TokenStream.TSF_RETURN_EXPR | TokenStream.TSF_RETURN_VOID);
0173: tokenStream.flags |= TokenStream.TSF_FUNCTION;
0174:
0175: try {
0176: statements();
0177: } finally {
0178: // also in finally block:
0179: // flushNewLines, clearPushback.
0180: tokenStream.flags = oldflags;
0181: }
0182: }//parseFunctionBody()
0183:
0184: private void function() throws IOException, JavaScriptException {
0185: if (tokenStream.matchesToken(TokenStream.NAME)) {
0186: //Bug No: 4742026
0187: //I.E Supports function names with DOT
0188: //e.g window.onload() etc..
0189: //may be while should be used if - Pending..
0190: //window.abc.onlod() etc.. to work..
0191: //use while as functions with name window.document.onLoad() etc
0192: //could be present
0193: while (tokenStream.matchesToken(TokenStream.DOT)
0194: && tokenStream.matchesToken(TokenStream.NAME)) {
0195: }
0196: //Bug No: 4742026
0197: }
0198: //else
0199: //{
0200: // // it's an anonymous function
0201: //}
0202:
0203: mustMatchToken(TokenStream.LP, "msg.no.paren.parms");
0204: if (!tokenStream.matchesToken(TokenStream.RP)) {
0205: do {
0206: mustMatchToken(TokenStream.NAME, "msg.no.parm");
0207: } while (tokenStream.matchesToken(TokenStream.COMMA));
0208:
0209: mustMatchToken(TokenStream.RP, "msg.no.paren.after.parms");
0210: }
0211:
0212: mustMatchToken(TokenStream.LC, "msg.no.brace.body");
0213: parseFunctionBody();
0214: mustMatchToken(TokenStream.RC, "msg.no.brace.after.body");
0215: }//function()
0216:
0217: private void statements() throws IOException, JavaScriptException {
0218: int tt;
0219: while ((tt = tokenStream.peekToken()) > TokenStream.EOF
0220: && tt != TokenStream.RC) {
0221: statement();
0222: }
0223: }//statements()
0224:
0225: private void condition() throws IOException, JavaScriptException {
0226: mustMatchToken(TokenStream.LP, "msg.no.paren.cond");
0227: expr(false);
0228: mustMatchToken(TokenStream.RP, "msg.no.paren.after.cond");
0229: }//condition()
0230:
0231: private boolean wellTerminated(final int lastExprType)
0232: throws IOException, JavaScriptException {
0233: final int tt = tokenStream.peekTokenSameLine();
0234: if (tt == TokenStream.ERROR) {
0235: reportError("msg.scanner.caught.error");
0236: return false;
0237: }
0238:
0239: if (tt != TokenStream.EOF && tt != TokenStream.EOL
0240: && tt != TokenStream.SEMI && tt != TokenStream.RC) {
0241: if (tt == TokenStream.FUNCTION
0242: || lastExprType == TokenStream.FUNCTION) {
0243: /*
0244: * Checking against version < 1.2 and version >= 1.0
0245: * in the above line breaks old javascript, so we keep it
0246: * this way for now... XXX warning needed?
0247: */
0248: return true;
0249: } else {
0250: reportError("msg.no.semi.stmt");
0251: }
0252: }
0253: return true;
0254: }//wellTerminated()
0255:
0256: // match a NAME; return null if no match.
0257: private String matchLabel() throws IOException, JavaScriptException {
0258: String label = null;
0259: final int tt = tokenStream.peekTokenSameLine();
0260:
0261: if (tt == TokenStream.NAME) {
0262: tokenStream.getToken();
0263: label = tokenStream.getString();
0264: }
0265:
0266: return label;
0267: }//matchLabel()
0268:
0269: private void statement() throws IOException, JavaScriptException {
0270: try {
0271: statementHelper();
0272: } catch (JavaScriptException e) {
0273: recordWarning(e);
0274: // skip to end of statement
0275: int t;
0276: do {
0277: t = tokenStream.getToken();
0278: } while (t != TokenStream.SEMI && t != TokenStream.EOL
0279: && t != TokenStream.EOF && t != TokenStream.ERROR);
0280: }
0281: }//statement()
0282:
0283: /**
0284: * Whether the "catch (e: e instanceof Exception) { ... }" syntax
0285: * is implemented.
0286: */
0287: private static final boolean implements CatchCond = false;
0288:
0289: private void statementHelper() throws IOException,
0290: JavaScriptException {
0291: int tt;
0292: int lastExprType = 0; // For wellTerminated. 0 to avoid warning.
0293:
0294: tt = tokenStream.getToken();
0295: switch (tt) {
0296: case TokenStream.IF: {
0297: condition();
0298: statement();
0299:
0300: if (tokenStream.matchesToken(TokenStream.ELSE)) {
0301: statement();
0302: }
0303:
0304: break;
0305: }
0306: case TokenStream.SWITCH: {
0307: mustMatchToken(TokenStream.LP, "msg.no.paren.switch");
0308: expr(false);
0309: mustMatchToken(TokenStream.RP, "msg.no.paren.after.switch");
0310: mustMatchToken(TokenStream.LC, "msg.no.brace.switch");
0311:
0312: while ((tt = tokenStream.getToken()) != TokenStream.RC
0313: && tt != TokenStream.EOF) {
0314: switch (tt) {
0315: case TokenStream.CASE: {
0316: expr(false);
0317: break;
0318: }
0319: case TokenStream.DEFAULT: {
0320: // XXX check that there isn't more than one default
0321: break;
0322: }
0323: default: {
0324: reportError("msg.bad.switch");
0325: break;
0326: }
0327: }
0328:
0329: mustMatchToken(TokenStream.COLON, "msg.no.colon.case");
0330:
0331: while ((tt = tokenStream.peekToken()) != TokenStream.RC
0332: && tt != TokenStream.CASE
0333: && tt != TokenStream.DEFAULT
0334: && tt != TokenStream.EOF) {
0335: statement();
0336: }
0337: }
0338:
0339: break;
0340: }
0341: case TokenStream.WHILE: {
0342: condition();
0343: statement();
0344: break;
0345: }
0346: case TokenStream.DO: {
0347: statement();
0348: mustMatchToken(TokenStream.WHILE, "msg.no.while.do");
0349: condition();
0350: break;
0351: }
0352: case TokenStream.FOR: {
0353: mustMatchToken(TokenStream.LP, "msg.no.paren.for");
0354: tt = tokenStream.peekToken();
0355:
0356: if (tt != TokenStream.SEMI) {
0357: if (tt == TokenStream.VAR) {
0358: // set init to a var list or initial
0359: tokenStream.getToken(); // throw away the 'var' token
0360: variables(true);
0361: } else {
0362: expr(true);
0363: }
0364: }
0365:
0366: tt = tokenStream.peekToken();
0367:
0368: if (tt == TokenStream.RELOP
0369: && tokenStream.getOp() == TokenStream.IN) {
0370: tokenStream.matchesToken(TokenStream.RELOP);
0371: // 'cond' is the object over which we're iterating
0372: expr(false);
0373: } else {
0374: // ordinary for loop
0375: mustMatchToken(TokenStream.SEMI, "msg.no.semi.for");
0376: if (tokenStream.peekToken() != TokenStream.SEMI) {
0377: expr(false);
0378: }
0379:
0380: mustMatchToken(TokenStream.SEMI, "msg.no.semi.for.cond");
0381:
0382: if (tokenStream.peekToken() != TokenStream.RP) {
0383: //}
0384: //else
0385: //{
0386: expr(false);
0387: }
0388: }
0389:
0390: mustMatchToken(TokenStream.RP, "msg.no.paren.for.ctrl");
0391: statement();
0392: break;
0393: }
0394: case TokenStream.TRY: {
0395: statement();
0396: int peek = tokenStream.peekToken();
0397: if (peek == TokenStream.CATCH) {
0398: while (tokenStream.matchesToken(TokenStream.CATCH)) {
0399: mustMatchToken(TokenStream.LP, "msg.no.paren.catch");
0400: mustMatchToken(TokenStream.NAME,
0401: "msg.bad.catchcond");
0402:
0403: if (implements CatchCond
0404: && tokenStream
0405: .matchesToken(TokenStream.COLON)) {
0406: expr(false);
0407: }
0408:
0409: mustMatchToken(TokenStream.RP, "msg.bad.catchcond");
0410: mustMatchToken(TokenStream.LC,
0411: "msg.no.brace.catchblock");
0412: statements();
0413: tokenStream.matchesToken(TokenStream.RC);
0414: }
0415: } else if (peek != TokenStream.FINALLY) {
0416: mustMatchToken(TokenStream.FINALLY,
0417: "msg.try.no.catchfinally");
0418: }
0419:
0420: if (tokenStream.matchesToken(TokenStream.FINALLY)) {
0421: statement();
0422: }
0423: break;
0424: }
0425: case TokenStream.THROW: {
0426: int lineno = tokenStream.getLineNo();
0427: expr(false);
0428:
0429: if (lineno == tokenStream.getLineNo()) {
0430: wellTerminated(TokenStream.ERROR);
0431: }
0432: break;
0433: }
0434: case TokenStream.BREAK: {
0435: // matchLabel only matches if there is one
0436: matchLabel(); //skip
0437: break;
0438: }
0439: case TokenStream.CONTINUE: {
0440: // matchLabel only matches if there is one
0441: matchLabel(); //skip
0442: break;
0443: }
0444: case TokenStream.WITH: {
0445: mustMatchToken(TokenStream.LP, "msg.no.paren.with");
0446: expr(false);
0447: mustMatchToken(TokenStream.RP, "msg.no.paren.after.with");
0448: statement();
0449: break;
0450: }
0451: case TokenStream.VAR: {
0452: variables(false);
0453: break;
0454: }
0455: case TokenStream.RETURN: {
0456: /* BugNo:4727580
0457: * As a part of normal JSParsing this is true as all the
0458: * HTML onClick, onSubmit is treated as functions, but
0459: * during our offline parsing, we simply treat the value
0460: * of these attributes as Javascript and hence
0461: * onSubmit="return abc('a')" can result in throwing of error
0462: */
0463: // bail if we're not in a (toplevel) function
0464: /*if ( ( tokenStream.flags & tokenStream.TSF_FUNCTION ) == 0 )
0465: {
0466: reportError( ts, "msg.bad.return" );
0467: }*/
0468: //BugNo:4727580
0469: /* This is ugly, but we don't want to require a semicolon. */
0470: tokenStream.flags |= TokenStream.TSF_REGEXP;
0471: tt = tokenStream.peekTokenSameLine();
0472: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0473: if (tt != TokenStream.EOF && tt != TokenStream.EOL
0474: && tt != TokenStream.SEMI && tt != TokenStream.RC) {
0475: expr(false);
0476: tokenStream.flags |= TokenStream.TSF_RETURN_EXPR;
0477: } else {
0478: tokenStream.flags |= TokenStream.TSF_RETURN_VOID;
0479: }
0480: break;
0481: }
0482: case TokenStream.LC: {
0483: statements();
0484: mustMatchToken(TokenStream.RC, "msg.no.brace.block");
0485: break;
0486: }
0487: case TokenStream.ERROR: {
0488: reportError("msg.scanner.caught.error");
0489: // Fall thru, to have a node for error recovery to work on
0490: }
0491: case TokenStream.EOL:
0492: case TokenStream.SEMI: {
0493: break;
0494: }
0495: default: {
0496: lastExprType = tt;
0497: int tokenno = tokenStream.getTokenNo();
0498: tokenStream.ungetToken(tt);
0499: expr(false);
0500: if (tokenStream.peekToken() == TokenStream.COLON) {
0501: /* check that the last thing the tokenizer returned was a
0502: * NAME and that only one token was consumed.
0503: */
0504: if (lastExprType != TokenStream.NAME
0505: || (tokenStream.getTokenNo() != tokenno)) {
0506: reportError("msg.bad.label");
0507: }
0508: tokenStream.getToken(); // eat the COLON
0509: return;
0510: }
0511:
0512: /*
0513: * Check explicitly against (multi-line) function
0514: * statement.
0515:
0516: * lastExprEndLine is a hack to fix an
0517: * automatic semicolon insertion problem with function
0518: * expressions; the tokenStream.getLineNo() == lineno check was
0519: * firing after a function definition even though the
0520: * next statement was on a new line, because
0521: * speculative getToken calls advanced the line number
0522: * even when they didn't succeed.
0523: */
0524:
0525: int tmpToken = tokenStream.getToken();
0526: tokenStream.ungetToken(tmpToken);
0527: if ((lastExprType == TokenStream.FUNCTION)
0528: && (tokenStream.getLineNo() == lastExprEndLine)) {
0529: wellTerminated(lastExprType);
0530: }
0531:
0532: break;
0533: }
0534: }
0535:
0536: tokenStream.matchesToken(TokenStream.SEMI);
0537: return;
0538: }//statementHelper()
0539:
0540: private void variables(final boolean inForInit) throws IOException,
0541: JavaScriptException {
0542: for (;;) {
0543: mustMatchToken(TokenStream.NAME, "msg.bad.var");
0544: // omitted check for argument hiding
0545: handleVariableMatch(tokenStream.getString(), inForInit,
0546: false);
0547: if (!tokenStream.matchesToken(TokenStream.COMMA)) {
0548: break;
0549: }
0550: }//for loop
0551: }//variables()
0552:
0553: private void expr(final boolean inForInit) throws IOException,
0554: JavaScriptException {
0555: assignExpr(inForInit, JSData.TRANSLATE_NONE);
0556: while (tokenStream.matchesToken(TokenStream.COMMA)) {
0557: assignExpr(inForInit, JSData.TRANSLATE_NONE);
0558: }
0559: }//expr()
0560:
0561: private void assignExpr(final boolean inForInit, final int aType)
0562: throws IOException, JavaScriptException {
0563: final StringBuffer vname = new StringBuffer();
0564: condExpr(inForInit, vname, aType);
0565: handleVariableMatch(vname.toString(), inForInit, false);
0566: }//assignExpr()
0567:
0568: private void condExpr(final boolean inForInit,
0569: final StringBuffer vname, final int aType)
0570: throws IOException, JavaScriptException {
0571: orExpr(inForInit, vname, aType);
0572: if (tokenStream.matchesToken(TokenStream.HOOK)) {
0573: assignExpr(false, aType);
0574: mustMatchToken(TokenStream.COLON, "msg.no.colon.cond");
0575: assignExpr(inForInit, aType);
0576: }
0577: }//condExpr()
0578:
0579: private void orExpr(final boolean inForInit,
0580: final StringBuffer vname, final int aType)
0581: throws IOException, JavaScriptException {
0582: andExpr(inForInit, vname, aType);
0583: if (tokenStream.matchesToken(TokenStream.OR)) {
0584: orExpr(inForInit, null, JSData.TRANSLATE_NONE);
0585: }
0586: }//orExpr()
0587:
0588: private void andExpr(final boolean inForInit,
0589: final StringBuffer vname, final int aType)
0590: throws IOException, JavaScriptException {
0591: bitOrExpr(inForInit, vname, aType);
0592: if (tokenStream.matchesToken(TokenStream.AND)) {
0593: andExpr(inForInit, null, JSData.TRANSLATE_NONE);
0594: }
0595: }//andExpr()
0596:
0597: private void bitOrExpr(final boolean inForInit,
0598: final StringBuffer vname, final int aType)
0599: throws IOException, JavaScriptException {
0600: bitXorExpr(inForInit, vname, aType);
0601: while (tokenStream.matchesToken(TokenStream.BITOR)) {
0602: bitXorExpr(inForInit, null, JSData.TRANSLATE_NONE);
0603: }
0604: }//bitOrExpr()
0605:
0606: private void bitXorExpr(final boolean inForInit,
0607: final StringBuffer vname, final int aType)
0608: throws IOException, JavaScriptException {
0609: bitAndExpr(inForInit, vname, aType);
0610: while (tokenStream.matchesToken(TokenStream.BITXOR)) {
0611: bitAndExpr(inForInit, null, JSData.TRANSLATE_NONE);
0612: }
0613: }//bitXorExpr()
0614:
0615: private void bitAndExpr(final boolean inForInit,
0616: final StringBuffer vname, final int aType)
0617: throws IOException, JavaScriptException {
0618: eqExpr(inForInit, vname, aType);
0619: while (tokenStream.matchesToken(TokenStream.BITAND)) {
0620: eqExpr(inForInit, null, JSData.TRANSLATE_NONE);
0621: }
0622: }//bitAndExpr()
0623:
0624: private void eqExpr(final boolean inForInit,
0625: final StringBuffer vname, final int aType)
0626: throws IOException, JavaScriptException {
0627: relExpr(inForInit, vname, aType);
0628: while (tokenStream.matchesToken(TokenStream.EQOP)) {
0629: relExpr(inForInit, null, JSData.TRANSLATE_NONE);
0630: }
0631: }//eqExpr()
0632:
0633: private void relExpr(final boolean inForInit,
0634: final StringBuffer vname, final int aType)
0635: throws IOException, JavaScriptException {
0636: shiftExpr(vname, aType);
0637: while (tokenStream.matchesToken(TokenStream.RELOP)) {
0638: int op = tokenStream.getOp();
0639: if (inForInit && op == TokenStream.IN) {
0640: tokenStream.ungetToken(TokenStream.RELOP);
0641: break;
0642: }
0643: shiftExpr(null, JSData.TRANSLATE_NONE);
0644: }
0645: }//relExpr()
0646:
0647: private void shiftExpr(final StringBuffer vname, final int aType)
0648: throws IOException, JavaScriptException {
0649: addExpr(vname, aType);
0650: while (tokenStream.matchesToken(TokenStream.SHOP)) {
0651: addExpr(null, JSData.TRANSLATE_NONE);
0652: }
0653: }//shiftExpr()
0654:
0655: private void addExpr(final StringBuffer vname, final int aType)
0656: throws IOException, JavaScriptException {
0657: int tt;
0658: mulExpr(vname, aType);
0659: while ((tt = tokenStream.getToken()) == TokenStream.ADD
0660: || tt == TokenStream.SUB) {
0661: // flushNewLines
0662: if (tt == TokenStream.SUB || aType == JSData.TRANSLATE_URL) {
0663: mulExpr(null, JSData.TRANSLATE_NONE);
0664: } else {
0665: mulExpr(null, aType);
0666: }
0667: }
0668: tokenStream.ungetToken(tt);
0669: }//addExpr()
0670:
0671: private void mulExpr(final StringBuffer vname, final int aType)
0672: throws IOException, JavaScriptException {
0673: int tt;
0674: unaryExpr(vname, aType);
0675: while ((tt = tokenStream.peekToken()) == TokenStream.MUL
0676: || tt == TokenStream.DIV || tt == TokenStream.MOD) {
0677: tokenStream.getToken(); //consume next token
0678: unaryExpr(null, JSData.TRANSLATE_NONE);
0679: }
0680: }//mulExpr()
0681:
0682: private void unaryExpr(final StringBuffer vname, final int aType)
0683: throws IOException, JavaScriptException {
0684: int tt;
0685: tokenStream.flags |= TokenStream.TSF_REGEXP;
0686: tt = tokenStream.getToken();
0687: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0688: switch (tt) {
0689: case TokenStream.UNARYOP: {
0690: unaryExpr(vname, JSData.TRANSLATE_NONE);
0691: return;
0692: }
0693: case TokenStream.ADD:
0694: case TokenStream.SUB: {
0695: unaryExpr(null, JSData.TRANSLATE_NONE);
0696: return;
0697: }
0698: case TokenStream.INC:
0699: case TokenStream.DEC: {
0700: memberExpr(true, vname, JSData.TRANSLATE_NONE);
0701: return;
0702: }
0703: case TokenStream.DELPROP: {
0704: unaryExpr(null, JSData.TRANSLATE_NONE);
0705: return;
0706: }
0707: case TokenStream.ERROR: {
0708: reportError("msg.scanner.caught.error");
0709: break;
0710: }
0711: default: {
0712: tokenStream.ungetToken(tt);
0713: memberExpr(true, vname, aType);
0714: tt = tokenStream.peekToken();
0715:
0716: if (!tokenStream.matchesToken(TokenStream.INC)) {
0717: tokenStream.matchesToken(TokenStream.DEC);
0718: }
0719:
0720: /* don't look across a newline boundary for a postfix incop.
0721:
0722: * the rhino scanner seems to work differently than the js
0723: * scanner here; in js, it works to have the line number check
0724: * precede the peekToken calls. It'd be better if they had
0725: * similar behavior...
0726: */
0727: return;
0728: }
0729: }
0730: return; // Only reached on error. Try to continue.
0731: }//unaryExpr()
0732:
0733: private void argumentList(final String fname) throws IOException,
0734: JavaScriptException {
0735:
0736: final boolean matched;
0737: tokenStream.flags |= TokenStream.TSF_REGEXP;
0738: matched = tokenStream.matchesToken(TokenStream.RP);
0739: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0740:
0741: if (!matched) {
0742: FunctionRule f = findFunctionMatch(fname, 10);
0743:
0744: if (f == null) {
0745: do {
0746: assignExpr(false, JSData.TRANSLATE_NONE);
0747: } while (tokenStream.matchesToken(TokenStream.COMMA));
0748: } else {
0749: String[] bParamSpec = f.getParamSpec(10);
0750: int bType = f.getFunction().getTypeCode();
0751:
0752: int argCount = 0;
0753: do {
0754: if (bParamSpec[argCount].equalsIgnoreCase("y")) {
0755: //BugNo:4965564
0756: if (bType == JSData.TRANSLATE_EXPRESSION
0757: || bType == JSData.TRANSLATE_DJS) {
0758: handleExpression(bType);
0759: } else {
0760: assignExpr(false, bType);
0761: }
0762: } else {
0763: assignExpr(false, JSData.TRANSLATE_NONE);
0764: }
0765:
0766: argCount++;
0767: } while (tokenStream.matchesToken(TokenStream.COMMA));
0768: }//if/else
0769:
0770: mustMatchToken(TokenStream.RP, "msg.no.paren.arg");
0771: }//if
0772: }//argumentList()
0773:
0774: private void memberExpr(final boolean allowCallSyntax,
0775: final StringBuffer vname, final int aType)
0776: throws IOException, JavaScriptException {
0777: final StringBuffer lFunName = new StringBuffer();
0778: boolean lFuncNameSupported = true;
0779: int tt;
0780:
0781: /* Check for new expressions. */
0782: tokenStream.flags |= TokenStream.TSF_REGEXP;
0783: tt = tokenStream.peekToken();
0784: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0785:
0786: if (tt == TokenStream.NEW) {
0787: tokenStream.getToken();
0788:
0789: /* Make a NEW node to append to. */
0790: StringBuffer obj_name = new StringBuffer();
0791: memberExpr(false, obj_name, JSData.TRANSLATE_NONE);
0792:
0793: if (tokenStream.matchesToken(TokenStream.LP)) {
0794: /* Add the arguments to pn, if any are supplied. */
0795: argumentList(obj_name.toString());
0796: }
0797: } else {
0798: primaryExpr(lFunName, aType);
0799: }
0800:
0801: int mark = tokenStream.getMark() - lFunName.length();
0802: lastExprEndLine = tokenStream.getLineNo();
0803:
0804: boolean isVariable = true;
0805: while ((tt = tokenStream.getToken()) > TokenStream.EOF) {
0806: if (tt == TokenStream.DOT) {
0807: isVariable = true;
0808: mustMatchToken(TokenStream.NAME,
0809: "msg.no.name.after.dot");
0810: lFunName.append(".").append(tokenStream.getString());//BugNo:4647942
0811: lastExprEndLine = tokenStream.getLineNo();
0812: } else if (tt == TokenStream.LB) {
0813: lFuncNameSupported = false;
0814: expr(false);
0815: mustMatchToken(TokenStream.RB, "msg.no.bracket.index");
0816: lastExprEndLine = tokenStream.getLineNo();
0817: } else if (allowCallSyntax && tt == TokenStream.LP) {
0818: isVariable = false;
0819: String ss;
0820: if (lFuncNameSupported) {
0821: ss = lFunName.toString();
0822: } else {
0823: ss = null;
0824: }
0825:
0826: /* Add the arguments to pn, if any are supplied. */
0827: argumentList(ss);
0828: lastExprEndLine = tokenStream.getLineNo();
0829: } else {
0830: tokenStream.ungetToken(tt);
0831: break;
0832: }
0833: }//while loop
0834:
0835: if (tt == TokenStream.ERROR) {
0836: reportError("msg.scanner.caught.error");
0837: return;
0838: }//if
0839:
0840: //BugNo:4787433
0841: if ((allowCallSyntax || isVariable) && vname != null) {
0842: vname.append(lFunName.toString());
0843: }
0844:
0845: //Make sure only RHS Values of System Var are wrapped and nothing else
0846: int nextToken = tokenStream.peekToken();
0847: if (nextToken != tokenStream.ASSIGN && isVariable) {
0848: if (lFunName.toString().trim().length() > 0) {
0849: VariableRule bMatch = RuleSetHelper
0850: .findJSSystemVariableMatch(ruleSet,
0851: variableMatche.recycleMatchee(lFunName
0852: .toString(),
0853: JSData.TRANSLATE_SYSTEM));
0854: if (bMatch != null) {
0855: handleVariableToken(mark, JSData.TRANSLATE_SYSTEM);
0856: } else if (REWRITE_VARS_IN_DHTML
0857: && aType == JSData.TRANSLATE_DHTML)//BugNo:4853583
0858:
0859: {
0860: bMatch = RuleSetHelper
0861: .findJSNonSystemVariableMatch(
0862: ruleSet,
0863: variableMatche
0864: .recycleMatchee(
0865: lFunName.toString(),
0866: JSData.TRANSLATE_EXPRESSION));
0867:
0868: if (bMatch != null) {
0869: handleVariableToken(mark,
0870: JSData.TRANSLATE_EXPRESSION);
0871: }
0872: }
0873: }//if
0874: }
0875: }//memberExpr()
0876:
0877: private void primaryExpr(final StringBuffer fname, final int aType)
0878: throws IOException, JavaScriptException {
0879: int tt;
0880: tokenStream.flags |= TokenStream.TSF_REGEXP;
0881: tt = tokenStream.getToken();
0882: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0883:
0884: switch (tt) {
0885: case TokenStream.FUNCTION: {
0886: function();
0887: return;
0888: }
0889: case TokenStream.LB: {
0890: tokenStream.flags |= TokenStream.TSF_REGEXP;
0891: boolean matched = tokenStream.matchesToken(TokenStream.RB);
0892: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0893:
0894: if (!matched) {
0895: do {
0896: tokenStream.flags |= TokenStream.TSF_REGEXP;
0897: tt = tokenStream.peekToken();
0898: tokenStream.flags &= ~TokenStream.TSF_REGEXP;
0899:
0900: if (tt == TokenStream.RB) {
0901: // to fix [,,,].length behavior...
0902: break;
0903: }
0904:
0905: if (tt != TokenStream.COMMA) {
0906: assignExpr(false, JSData.TRANSLATE_NONE);
0907: }
0908:
0909: } while (tokenStream.matchesToken(TokenStream.COMMA));
0910: mustMatchToken(TokenStream.RB, "msg.no.bracket.arg");
0911: }
0912: return;
0913: }
0914: case TokenStream.LC: {
0915: if (!tokenStream.matchesToken(TokenStream.RC)) {
0916: commaloop: do {
0917: tt = tokenStream.getToken();
0918:
0919: switch (tt) {
0920: case TokenStream.NAME:
0921: case TokenStream.STRING:
0922: case TokenStream.NUMBER: {
0923: break;
0924: }
0925: case TokenStream.RC: {
0926: // trailing comma is OK.
0927: tokenStream.ungetToken(tt);
0928: break commaloop;
0929: }
0930: default: {
0931: reportError("msg.bad.prop");
0932: break commaloop;
0933: }
0934: }
0935:
0936: mustMatchToken(TokenStream.COLON,
0937: "msg.no.colon.prop");
0938: //Object Property rewriting. BugNo:4905670
0939: handleVariableMatch(tokenStream.getString(), false,
0940: true);
0941: } while (tokenStream.matchesToken(TokenStream.COMMA));
0942: mustMatchToken(TokenStream.RC, "msg.no.brace.prop");
0943: }
0944: return;
0945: }
0946: case TokenStream.LP: {
0947: expr(false);
0948: mustMatchToken(TokenStream.RP, "msg.no.paren");
0949: return;
0950: }
0951: case TokenStream.NAME: {
0952: String name = tokenStream.getString();
0953: fname.append(name);
0954: return;
0955: }
0956: case TokenStream.NUMBER: {
0957: return;
0958: }
0959: case TokenStream.STRING: {
0960: if (aType != JSData.TRANSLATE_NONE) {
0961: handleStringTokens(aType);
0962: }
0963:
0964: return;
0965: }
0966: case TokenStream.OBJECT: {
0967: return;
0968: }
0969: case TokenStream.PRIMARY: {
0970: String primaryStr = tokenStream.getString();
0971:
0972: if (primaryStr.equals("this") || primaryStr.equals("super")) {
0973: fname.append(primaryStr);
0974: }
0975:
0976: return;
0977: }
0978: case TokenStream.RESERVED: {
0979: reportError("msg.reserved.id");
0980: break;
0981: }
0982: case TokenStream.ERROR: {
0983: /* the scanner or one of its subroutines reported the error. */
0984: reportError("msg.scanner.caught.error");
0985: break;
0986: }
0987: default: {
0988: //BugNo:4755898 Varient - Accept '-->' as a part of javascript
0989: if (tt == TokenStream.RELOP) {
0990: return;
0991: }
0992: //BugNo:4755898 Varient
0993:
0994: reportError("msg.syntax");
0995: break;
0996: }
0997: }
0998:
0999: return; // should never reach here
1000: }//primaryExpr()
1001:
1002: private void handleVariableMatch(final String aVariableName,
1003: final boolean inForInit, final boolean isObjectProperty)
1004: throws IOException, JavaScriptException {
1005: if (isObjectProperty
1006: || tokenStream.matchesToken(TokenStream.ASSIGN)) {
1007: int aType = JSData.TRANSLATE_NONE;
1008:
1009: VariableRule v = findVariableMatch(aVariableName);
1010: if (v != null) {
1011: aType = v.getVariable().getTypeCode();
1012:
1013: //BugNo: 4634502 - Just Skip checking this.
1014: //when it is += -= etc.. do this only URL && Expression Type variables
1015: //and not for DHTML, DJS
1016: if ((aType == JSData.TRANSLATE_URL || aType == JSData.TRANSLATE_EXPRESSION)
1017: && tokenStream.getOp() != TokenStream.NOP) {
1018: assignExpr(inForInit, JSData.TRANSLATE_NONE);
1019: return;
1020: }
1021: //BugNo:4965564
1022: if (aType == JSData.TRANSLATE_EXPRESSION
1023: || aType == JSData.TRANSLATE_DJS) {
1024: handleExpression(aType);
1025: return; //Bug No: 4691210
1026: }
1027: }
1028:
1029: assignExpr(inForInit, aType);
1030: }//if
1031: }//handleVariableMatch()
1032:
1033: private VariableRule findVariableMatch(final String aVariableName) {
1034: //Bug4730531
1035: if (aVariableName == null || aVariableName.length() == 0) {
1036: return null;
1037: }
1038:
1039: return RuleSetHelper.findJSNonSystemVariableMatch(ruleSet,
1040: variableMatche.recycleMatchee(aVariableName,
1041: VariableRule.ANY));
1042: }//findVariableMatch()
1043:
1044: private FunctionRule findFunctionMatch(final String aFunctionName,
1045: final int aParamCount) {
1046: //Bug4730531
1047: if (aFunctionName == null || aFunctionName.length() == 0) {
1048: return null;
1049: }
1050:
1051: return RuleSetHelper.findJSFunctionMatch(ruleSet,
1052: functionMatche.recycleMatchee(aFunctionName,
1053: aParamCount));
1054: }//findFunctionMatch()
1055:
1056: private void handleVariableToken(final int mark, final int aType) {
1057: int mark2 = tokenStream.getMark();
1058: doRewrite(mark, mark2, aType);
1059: }//handleVariableToken()
1060:
1061: private void doRewrite(int aMark1, int aMark2, final int aType) {
1062: String value2Rewrite = resultBuffer.substring(aMark1, aMark2);
1063: String rewrittenContent = value2Rewrite;
1064: if (value2Rewrite.trim().length() != 0) {
1065: rewrittenContent = rewriteByType(value2Rewrite, aType);
1066: resultBuffer.replace(aMark1, aMark2, rewrittenContent);
1067: }
1068:
1069: tokenStream.updateMark(aMark2 + rewrittenContent.length()
1070: - value2Rewrite.length());
1071: }//doRewrite()
1072:
1073: private void handleExpression(final int aType) throws IOException,
1074: JavaScriptException {
1075: int mark1 = tokenStream.getMark();
1076: assignExpr(false, JSData.TRANSLATE_NONE);
1077:
1078: int mark2 = tokenStream.getMark();
1079: doRewrite(mark1, mark2, aType);
1080: }//handleExpression()
1081:
1082: private void handleStringTokens(final int aType) {
1083: int[] spec = tokenStream.getStringSpec();
1084: String value2Rewrite = resultBuffer.substring(spec[4], spec[5]);
1085: if (spec[2] > spec[0]) {
1086: value2Rewrite = StringHelper.searchAndReplace(
1087: value2Rewrite, "\\\n", "");
1088: }
1089: resultBuffer.replace(spec[4], spec[5], rewriteByType(
1090: value2Rewrite, aType));
1091: }//handleStringTokens()
1092:
1093: private final String rewriteByType(final String aValue2Rewrite,
1094: final int aType) {
1095: String trimmedValue = aValue2Rewrite.trim();
1096: if (trimmedValue.length() == 0) //BugNo:4865959
1097: {
1098: return aValue2Rewrite;
1099: }
1100:
1101: switch (aType) {
1102: case JSData.TRANSLATE_URL: {
1103: return jsRewriter.translateURL(aValue2Rewrite, translator);
1104: }
1105: case JSData.TRANSLATE_EXPRESSION: {
1106: return jsRewriter.translateEXPRESSION(aValue2Rewrite,
1107: translator);
1108: }
1109: case JSData.TRANSLATE_DHTML: {
1110: return jsRewriter
1111: .translateDHTML(aValue2Rewrite, translator);
1112: }
1113: case JSData.TRANSLATE_DJS: {
1114: return jsRewriter.translateDJS(aValue2Rewrite, translator);
1115: }
1116: case JSData.TRANSLATE_SYSTEM: {
1117: return jsRewriter.translateSYSTEM(aValue2Rewrite,
1118: translator);
1119: }
1120: }//switch
1121:
1122: return null;
1123: }//rewriteaType()
1124:
1125: static String getMessage(final String aMessageID,
1126: final Object[] aArgs) {
1127: return "JavaScript compile error: id = " + aMessageID
1128: + ", args = " + aArgs;
1129: }//getMessage()
1130:
1131: public static void recordWarning(JavaScriptException e) {
1132: if (Debug.isWarningEnabled()) {
1133: //Debug.recordOriginalPageWarning( "Unable to Translate JS:", e );
1134: Debug.recordOriginalPageWarning("PSRW_CSPR_0020", e);
1135: }
1136: }//recordWarning()
1137:
1138: public static void main(String[] args) throws Exception {
1139: /*final String[][] basicTestData =
1140: {
1141: {"this.sessURL = prot + \"http://myhost.domain.com:81\" + \"/scripts/cgi\";"},
1142: };
1143:
1144: String lRules = "<Variable name=\"this.sessURL\" type=\"EXPRESSION\"/>";
1145:
1146: for ( int i = 0; i < basicTestData.length; i++ )
1147: {
1148: RuleSet lRuleSet = CreateRuleSet.withJSVariableRules( lRules );
1149: JSParser p = new JSParser( new PageContent( basicTestData[i][0] ),
1150: lRuleSet,
1151: new AbsoluteTranslator(
1152: new PageSpec( "http://raja.sun.com/ab.html?eee" ) ), null );
1153: Debug.println( "SourceURI: " + basicTestData[i][0] );
1154: Debug.println( "Input: " + basicTestData[i][1] );
1155: }//for loop*/
1156: }//main()
1157:
1158: }//class JSParser
|