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 org.jetbrains.annotations.NotNull;
009    
010    /**
011     * Searches for method invocations in constructor expressions that would not
012     * normally be surrounded by parentheses.
013     */
014    public class JsConstructExpressionVisitor extends RecursiveJsVisitor {
015        public static boolean exec(JsExpression expression) {
016            if (JsPrecedenceVisitor.exec(expression) < JsPrecedenceVisitor.PRECEDENCE_NEW) {
017                return true;
018            }
019            JsConstructExpressionVisitor visitor = new JsConstructExpressionVisitor();
020            visitor.accept(expression);
021            return visitor.containsInvocation;
022        }
023    
024        private boolean containsInvocation;
025    
026        private JsConstructExpressionVisitor() {
027        }
028    
029        /**
030         * We only look at the array expression since the index has its own scope.
031         */
032        @Override
033        public void visitArrayAccess(@NotNull JsArrayAccess x) {
034            accept(x.getArrayExpression());
035        }
036    
037        /**
038         * Array literals have their own scoping.
039         */
040        @Override
041        public void visitArray(@NotNull JsArrayLiteral x) {
042        }
043    
044        /**
045         * Functions have their own scoping.
046         */
047        @Override
048        public void visitFunction(@NotNull JsFunction x) {
049        }
050    
051        @Override
052        public void visitInvocation(@NotNull JsInvocation invocation) {
053            containsInvocation = true;
054        }
055    
056        @Override
057        public void visitNameRef(@NotNull JsNameRef nameRef) {
058            if (!nameRef.isLeaf()) {
059                accept(nameRef.getQualifier());
060            }
061        }
062    
063        /**
064         * New constructs bind to the nearest set of parentheses.
065         */
066        @Override
067        public void visitNew(@NotNull JsNew x) {
068        }
069    
070        /**
071         * Object literals have their own scope.
072         */
073        @Override
074        public void visitObjectLiteral(@NotNull JsObjectLiteral x) {
075        }
076    
077        /**
078         * We only look at nodes that would not normally be surrounded by parentheses.
079         */
080        @Override
081        public <T extends JsNode> void accept(T node) {
082            // Assign to Object to prevent 'inconvertible types' compile errors due
083            // to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6548436
084            // reproducible in jdk1.6.0_02.
085            if (node instanceof JsExpression) {
086                JsExpression expression = (JsExpression) node;
087                int precedence = JsPrecedenceVisitor.exec(expression);
088                // Only visit expressions that won't automatically be surrounded by
089                // parentheses
090                if (precedence < JsPrecedenceVisitor.PRECEDENCE_NEW) {
091                    return;
092                }
093            }
094            super.accept(node);
095        }
096    }