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 }