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 }