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