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.JsArrayAccess;
019: import com.google.gwt.dev.js.ast.JsArrayLiteral;
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.JsFunction;
023: import com.google.gwt.dev.js.ast.JsInvocation;
024: import com.google.gwt.dev.js.ast.JsNameRef;
025: import com.google.gwt.dev.js.ast.JsNew;
026: import com.google.gwt.dev.js.ast.JsObjectLiteral;
027: import com.google.gwt.dev.js.ast.JsVisitable;
028: import com.google.gwt.dev.js.ast.JsVisitor;
029:
030: /**
031: * Searches for method invocations in constructor expressions that would not
032: * normally be surrounded by parentheses.
033: */
034: public class JsConstructExpressionVisitor extends JsVisitor {
035:
036: private static final int PRECEDENCE_NEW = JsPrecedenceVisitor
037: .exec(new JsNew());
038:
039: public static boolean exec(JsExpression expression) {
040: if (JsPrecedenceVisitor.exec(expression) < PRECEDENCE_NEW) {
041: return true;
042: }
043: JsConstructExpressionVisitor visitor = new JsConstructExpressionVisitor();
044: visitor.accept(expression);
045: return visitor.containsInvocation;
046: }
047:
048: private boolean containsInvocation = false;
049:
050: private JsConstructExpressionVisitor() {
051: }
052:
053: /**
054: * We only look at the array expression since the index has its own scope.
055: */
056: @Override
057: public boolean visit(JsArrayAccess x, JsContext<JsExpression> ctx) {
058: accept(x.getArrayExpr());
059: return false;
060: }
061:
062: /**
063: * Array literals have their own scoping.
064: */
065: @Override
066: public boolean visit(JsArrayLiteral x, JsContext<JsExpression> ctx) {
067: return false;
068: }
069:
070: /**
071: * Functions have their own scoping.
072: */
073: @Override
074: public boolean visit(JsFunction x, JsContext<JsExpression> ctx) {
075: return false;
076: }
077:
078: @Override
079: public boolean visit(JsInvocation x, JsContext<JsExpression> ctx) {
080: containsInvocation = true;
081: return false;
082: }
083:
084: @Override
085: public boolean visit(JsNameRef x, JsContext<JsExpression> ctx) {
086: if (!x.isLeaf()) {
087: accept(x.getQualifier());
088: }
089: return false;
090: }
091:
092: /**
093: * New constructs bind to the nearest set of parentheses.
094: */
095: @Override
096: public boolean visit(JsNew x, JsContext<JsExpression> ctx) {
097: return false;
098: }
099:
100: /**
101: * Object literals have their own scope.
102: */
103: @Override
104: public boolean visit(JsObjectLiteral x, JsContext<JsExpression> ctx) {
105: return false;
106: }
107:
108: /**
109: * We only look at nodes that would not normally be surrounded by parentheses.
110: */
111: protected <T extends JsVisitable<T>> T doAccept(T node) {
112: /*
113: * Extra casts to Object to prevent 'inconvertible types' compile errors due
114: * to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6548436
115: * reproducible in jdk1.6.0_02.
116: */
117: if ((Object) node instanceof JsExpression) {
118: JsExpression expression = (JsExpression) (Object) node;
119: int precedence = JsPrecedenceVisitor.exec(expression);
120: // Only visit expressions that won't automatically be surrounded by
121: // parentheses
122: if (precedence < PRECEDENCE_NEW) {
123: return node;
124: }
125: }
126: return super.doAccept(node);
127: }
128:
129: }
|