001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.js;
017:
018: import com.google.gwt.dev.js.ast.JsBinaryOperation;
019: import com.google.gwt.dev.js.ast.JsBinaryOperator;
020: import com.google.gwt.dev.js.ast.JsContext;
021: import com.google.gwt.dev.js.ast.JsExpression;
022: import com.google.gwt.dev.js.ast.JsModVisitor;
023: import com.google.gwt.dev.js.ast.JsPostfixOperation;
024: import com.google.gwt.dev.js.ast.JsPrefixOperation;
025: import com.google.gwt.dev.js.ast.JsProgram;
026: import com.google.gwt.dev.js.ast.JsUnaryOperation;
027: import com.google.gwt.dev.js.ast.JsUnaryOperator;
028:
029: /**
030: * Fixes any semantic errors introduced by JS AST gen.
031: *
032: * <ul>
033: * <li> Creating clinit calls can put comma expressions as lvalues; the
034: * modifying operation must be moved inside the comma expression to the last
035: * argument. </li>
036: * </ul>
037: */
038: public class JsNormalizer {
039:
040: /**
041: * Resolves any unresolved JsNameRefs.
042: */
043: private class JsNormalizing extends JsModVisitor {
044:
045: public void endVisit(JsBinaryOperation x,
046: JsContext<JsExpression> ctx) {
047: maybeShuffleModifyingBinary(x, ctx);
048: }
049:
050: public void endVisit(JsPostfixOperation x,
051: JsContext<JsExpression> ctx) {
052: maybeShuffleModifyingUnary(x, ctx);
053: }
054:
055: public void endVisit(JsPrefixOperation x,
056: JsContext<JsExpression> ctx) {
057: maybeShuffleModifyingUnary(x, ctx);
058: }
059:
060: /**
061: * Due to the way clinits are constructed, you can end up with a comma
062: * operation as the argument to a modifying operation, which is illegal.
063: * Juggle things to put the operator inside of the comma expression.
064: */
065: private void maybeShuffleModifyingBinary(JsBinaryOperation x,
066: JsContext<JsExpression> ctx) {
067: JsBinaryOperator myOp = x.getOperator();
068: JsExpression lhs = x.getArg1();
069:
070: if (myOp.isAssignment()
071: && (lhs instanceof JsBinaryOperation)) {
072: // Find the rightmost comma operation
073: JsBinaryOperation curLhs = (JsBinaryOperation) lhs;
074: assert (curLhs.getOperator() == JsBinaryOperator.COMMA);
075: while (curLhs.getArg2() instanceof JsBinaryOperation) {
076: curLhs = (JsBinaryOperation) curLhs.getArg2();
077: assert (curLhs.getOperator() == JsBinaryOperator.COMMA);
078: }
079: // curLhs is now the rightmost comma operation; slide our operation in
080: x.setArg1(curLhs.getArg2());
081: curLhs.setArg2(x);
082: // replace myself with the comma expression
083: ctx.replaceMe(lhs);
084: }
085: }
086:
087: /**
088: * Due to the way clinits are constructed, you can end up with a comma
089: * operation as the argument to a modifying operation, which is illegal.
090: * Juggle things to put the operator inside of the comma expression.
091: */
092: private void maybeShuffleModifyingUnary(JsUnaryOperation x,
093: JsContext<JsExpression> ctx) {
094: JsUnaryOperator myOp = x.getOperator();
095: JsExpression arg = x.getArg();
096: if (myOp.isModifying()
097: && (arg instanceof JsBinaryOperation)) {
098: // Find the rightmost comma operation
099: JsBinaryOperation curArg = (JsBinaryOperation) arg;
100: assert (curArg.getOperator() == JsBinaryOperator.COMMA);
101: while (curArg.getArg2() instanceof JsBinaryOperation) {
102: curArg = (JsBinaryOperation) curArg.getArg2();
103: assert (curArg.getOperator() == JsBinaryOperator.COMMA);
104: }
105: // curArg is now the rightmost comma operation; slide our operation in
106: x.setArg(curArg.getArg2());
107: curArg.setArg2(x);
108: // replace myself with the comma expression
109: ctx.replaceMe(arg);
110: }
111: }
112: }
113:
114: public static void exec(JsProgram program) {
115: new JsNormalizer(program).execImpl();
116: }
117:
118: private final JsProgram program;
119:
120: private JsNormalizer(JsProgram program) {
121: this .program = program;
122: }
123:
124: private void execImpl() {
125: JsNormalizing normalizer = new JsNormalizing();
126: normalizer.accept(program);
127: }
128: }
|