001 /*
002 * Copyright 2008 Google Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015 package com.google.dart.compiler.backend.js.ast;
016
017 /**
018 * Taken from GWT project with modifications.
019 * Original:
020 * repository: https://gwt.googlesource.com/gwt
021 * revision: e32bf0a95029165d9e6ab457c7ee7ca8b07b908c
022 * file: dev/core/src/com/google/gwt/dev/js/ast/JsModVisitor.java
023 */
024
025 import com.intellij.util.SmartList;
026 import org.jetbrains.annotations.NotNull;
027 import org.jetbrains.annotations.Nullable;
028
029 import java.util.List;
030 import java.util.Stack;
031
032 /**
033 * A visitor for iterating through and modifying an AST.
034 */
035 public class JsVisitorWithContextImpl extends JsVisitorWithContext {
036
037 private final Stack<JsContext> statementContexts = new Stack<JsContext>();
038
039 public class ListContext<T extends JsNode> implements JsContext {
040 private List<T> collection;
041 private int index;
042
043 @Override
044 public boolean canInsert() {
045 return true;
046 }
047
048 @Override
049 public boolean canRemove() {
050 return true;
051 }
052
053 @Override
054 public void insertAfter(JsNode node) {
055 //noinspection unchecked
056 collection.add(index + 1, (T) node);
057 }
058
059 @Override
060 public void insertBefore(JsNode node) {
061 //noinspection unchecked
062 collection.add(index++, (T) node);
063 }
064
065 @Override
066 public boolean isLvalue() {
067 return false;
068 }
069
070 @Override
071 public void removeMe() {
072 collection.remove(index--);
073 }
074
075 @Override
076 public void replaceMe(JsNode node) {
077 checkReplacement(collection.get(index), node);
078 //noinspection unchecked
079 collection.set(index, (T) node);
080 }
081
082 @Nullable
083 @Override
084 public JsNode getCurrentNode() {
085 if (index < collection.size()) {
086 return collection.get(index);
087 }
088
089 return null;
090 }
091
092 protected void traverse(List<T> collection) {
093 this.collection = collection;
094 for (index = 0; index < collection.size(); ++index) {
095 T node = collection.get(index);
096 doTraverse(node, this);
097 }
098 }
099 }
100
101 private class LvalueContext extends NodeContext<JsExpression> {
102 @Override
103 public boolean isLvalue() {
104 return true;
105 }
106 }
107
108 @SuppressWarnings("unchecked")
109 private class NodeContext<T extends JsNode> implements JsContext {
110 protected T node;
111
112 @Override
113 public boolean canInsert() {
114 return false;
115 }
116
117 @Override
118 public boolean canRemove() {
119 return false;
120 }
121
122 @Override
123 public void insertAfter(JsNode node) {
124 throw new UnsupportedOperationException();
125 }
126
127 @Override
128 public void insertBefore(JsNode node) {
129 throw new UnsupportedOperationException();
130 }
131
132 @Override
133 public boolean isLvalue() {
134 return false;
135 }
136
137 @Override
138 public void removeMe() {
139 throw new UnsupportedOperationException();
140 }
141
142 @Override
143 public void replaceMe(JsNode node) {
144 checkReplacement(this.node, node);
145 this.node = (T) node;
146 }
147
148 @Nullable
149 @Override
150 public JsNode getCurrentNode() {
151 return node;
152 }
153
154 protected T traverse(T node) {
155 this.node = node;
156 doTraverse(node, this);
157 return this.node;
158 }
159 }
160
161 protected static void checkReplacement(@SuppressWarnings("UnusedParameters") JsNode origNode, JsNode newNode) {
162 if (newNode == null) throw new RuntimeException("Cannot replace with null");
163 }
164
165 @Override
166 protected <T extends JsNode> T doAccept(T node) {
167 return new NodeContext<T>().traverse(node);
168 }
169
170 @Override
171 protected JsExpression doAcceptLvalue(JsExpression expr) {
172 return new LvalueContext().traverse(expr);
173 }
174
175 @Override
176 protected <T extends JsStatement> JsStatement doAcceptStatement(T statement) {
177 List<JsStatement> statements = new SmartList<JsStatement>(statement);
178 doAcceptStatementList(statements);
179
180 if (statements.size() == 1) {
181 return statements.get(0);
182 }
183
184 return new JsBlock(statements);
185 }
186
187 @Override
188 protected <T extends JsStatement> void doAcceptStatementList(List<T> statements) {
189 ListContext<T> context = new ListContext<T>();
190 statementContexts.push(context);
191 context.traverse(statements);
192 statementContexts.pop();
193 }
194
195 @Override
196 protected <T extends JsNode> void doAcceptList(List<T> collection) {
197 new ListContext<T>().traverse(collection);
198 }
199
200 @NotNull
201 protected JsContext getLastStatementLevelContext() {
202 return statementContexts.peek();
203 }
204
205 @Override
206 protected <T extends JsNode> void doTraverse(T node, JsContext ctx) {
207 node.traverse(this, ctx);
208 }
209
210 }