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    import org.jetbrains.annotations.NotNull;
010    
011    /**
012     * Determines if an expression statement needs to be surrounded by parentheses.
013     * <p/>
014     * The statement or the left-most expression needs to be surrounded by
015     * parentheses if the left-most expression is an object literal or a function
016     * object. Function declarations do not need parentheses.
017     * <p/>
018     * For example the following require parentheses:<br>
019     * <ul>
020     * <li>{ key : 'value'}</li>
021     * <li>{ key : 'value'}.key</li>
022     * <li>function () {return 1;}()</li>
023     * <li>function () {return 1;}.prototype</li>
024     * </ul>
025     * <p/>
026     * The following do not require parentheses:<br>
027     * <ul>
028     * <li>var x = { key : 'value'}</li>
029     * <li>"string" + { key : 'value'}.key</li>
030     * <li>function func() {}</li>
031     * <li>function() {}</li>
032     * </ul>
033     */
034    public class JsFirstExpressionVisitor extends RecursiveJsVisitor {
035        public static boolean exec(JsExpressionStatement statement) {
036            JsExpression expression = statement.getExpression();
037            // Pure function declarations do not need parentheses
038            if (expression instanceof JsFunction) {
039                return false;
040            }
041    
042            JsFirstExpressionVisitor visitor = new JsFirstExpressionVisitor();
043            visitor.accept(statement.getExpression());
044            return visitor.needsParentheses;
045        }
046    
047        private boolean needsParentheses = false;
048    
049        private JsFirstExpressionVisitor() {
050        }
051    
052        @Override
053        public void visitArrayAccess(@NotNull JsArrayAccess x) {
054            accept(x.getArrayExpression());
055        }
056    
057        @Override
058        public void visitArray(@NotNull JsArrayLiteral x) {
059        }
060    
061        @Override
062        public void visitBinaryExpression(@NotNull JsBinaryOperation x) {
063            accept(x.getArg1());
064        }
065    
066        @Override
067        public void visitConditional(@NotNull JsConditional x) {
068            accept(x.getTestExpression());
069        }
070    
071        @Override
072        public void visitFunction(@NotNull JsFunction x) {
073            needsParentheses = true;
074        }
075    
076        @Override
077        public void visitInvocation(@NotNull JsInvocation invocation) {
078            accept(invocation.getQualifier());
079        }
080    
081        @Override
082        public void visitNameRef(@NotNull JsNameRef nameRef) {
083            if (!nameRef.isLeaf()) {
084                accept(nameRef.getQualifier());
085            }
086        }
087    
088        @Override
089        public void visitNew(@NotNull JsNew x) {
090        }
091    
092        @Override
093        public void visitObjectLiteral(@NotNull JsObjectLiteral x) {
094            needsParentheses = true;
095        }
096    
097        @Override
098        public void visitPostfixOperation(@NotNull JsPostfixOperation x) {
099            accept(x.getArg());
100        }
101    
102        @Override
103        public void visitPrefixOperation(@NotNull JsPrefixOperation x) {
104        }
105    }