001    // Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
002    // for details. All rights reserved. Use of this source code is governed by a
003    // BSD-style license that can be found in the LICENSE file.
004    
005    package com.google.dart.compiler.backend.js;
006    
007    import com.google.dart.compiler.backend.js.ast.*;
008    import com.google.dart.compiler.backend.js.ast.JsExpressionStatement;
009    
010    /**
011     * Determines if an expression statement needs to be surrounded by parentheses.
012     * <p/>
013     * The statement or the left-most expression needs to be surrounded by
014     * parentheses if the left-most expression is an object literal or a function
015     * object. Function declarations do not need parentheses.
016     * <p/>
017     * For example the following require parentheses:<br>
018     * <ul>
019     * <li>{ key : 'value'}</li>
020     * <li>{ key : 'value'}.key</li>
021     * <li>function () {return 1;}()</li>
022     * <li>function () {return 1;}.prototype</li>
023     * </ul>
024     * <p/>
025     * The following do not require parentheses:<br>
026     * <ul>
027     * <li>var x = { key : 'value'}</li>
028     * <li>"string" + { key : 'value'}.key</li>
029     * <li>function func() {}</li>
030     * <li>function() {}</li>
031     * </ul>
032     */
033    public class JsFirstExpressionVisitor extends RecursiveJsVisitor {
034        public static boolean exec(JsExpressionStatement statement) {
035            JsExpression expression = statement.getExpression();
036            // Pure function declarations do not need parentheses
037            if (expression instanceof JsFunction) {
038                return false;
039            }
040    
041            JsFirstExpressionVisitor visitor = new JsFirstExpressionVisitor();
042            visitor.accept(statement.getExpression());
043            return visitor.needsParentheses;
044        }
045    
046        private boolean needsParentheses = false;
047    
048        private JsFirstExpressionVisitor() {
049        }
050    
051        @Override
052        public void visitArrayAccess(JsArrayAccess x) {
053            accept(x.getArrayExpression());
054        }
055    
056        @Override
057        public void visitArray(JsArrayLiteral x) {
058        }
059    
060        @Override
061        public void visitBinaryExpression(JsBinaryOperation x) {
062            accept(x.getArg1());
063        }
064    
065        @Override
066        public void visitConditional(JsConditional x) {
067            accept(x.getTestExpression());
068        }
069    
070        @Override
071        public void visitFunction(JsFunction x) {
072            needsParentheses = true;
073        }
074    
075        @Override
076        public void visitInvocation(JsInvocation invocation) {
077            accept(invocation.getQualifier());
078        }
079    
080        @Override
081        public void visitNameRef(JsNameRef nameRef) {
082            if (!nameRef.isLeaf()) {
083                accept(nameRef.getQualifier());
084            }
085        }
086    
087        @Override
088        public void visitNew(JsNew x) {
089        }
090    
091        @Override
092        public void visitObjectLiteral(JsObjectLiteral x) {
093            needsParentheses = true;
094        }
095    
096        @Override
097        public void visitPostfixOperation(JsPostfixOperation x) {
098            accept(x.getArg());
099        }
100    
101        @Override
102        public void visitPrefixOperation(JsPrefixOperation x) {
103        }
104    }