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.*;
030
031 /**
032 * A visitor for iterating through and modifying an AST.
033 */
034 public class JsVisitorWithContextImpl extends JsVisitorWithContext {
035
036 private final Stack<JsContext<JsStatement>> statementContexts = new Stack<JsContext<JsStatement>>();
037
038 public class ListContext<T extends JsNode> extends JsContext<T> {
039 private List<T> nodes;
040 private int index;
041
042 // Those are reset in every iteration of traverse()
043 private final List<T> previous = new SmartList<T>();
044 private final List<T> next = new SmartList<T>();
045 private boolean removed = false;
046
047 @Override
048 public <R extends T> void addPrevious(R node) {
049 previous.add(node);
050 }
051
052 @Override
053 public <R extends T> void addNext(R node) {
054 next.add(node);
055 }
056
057 @Override
058 public void removeMe() {
059 removed = true;
060 }
061
062 @Override
063 public <R extends T> void replaceMe(R node) {
064 checkReplacement(nodes.get(index), node);
065 nodes.set(index, node);
066 removed = false;
067 }
068
069 @Nullable
070 @Override
071 public T getCurrentNode() {
072 if (!removed && index < nodes.size()) {
073 return nodes.get(index);
074 }
075
076 return null;
077 }
078
079 protected void traverse(List<T> nodes) {
080 assert previous.isEmpty(): "addPrevious() was called before traverse()";
081 assert next.isEmpty(): "addNext() was called before traverse()";
082 this.nodes = nodes;
083
084 for (index = 0; index < nodes.size(); index++) {
085 removed = false;
086 previous.clear();
087 next.clear();
088 doTraverse(getCurrentNode(), this);
089
090 if (!previous.isEmpty()) {
091 nodes.addAll(index, previous);
092 index += previous.size();
093 }
094
095 if (removed) {
096 nodes.remove(index);
097 index--;
098 }
099
100 if (!next.isEmpty()) {
101 nodes.addAll(index + 1, next);
102 index += next.size();
103 }
104 }
105 }
106 }
107
108 private class LvalueContext extends NodeContext<JsExpression> {
109 }
110
111 private class NodeContext<T extends JsNode> extends JsContext<T> {
112 protected T node;
113
114 @Override
115 public void removeMe() {
116 throw new UnsupportedOperationException();
117 }
118
119 @Override
120 public <R extends T> void replaceMe(R node) {
121 checkReplacement(this.node, node);
122 this.node = node;
123 }
124
125 @Nullable
126 @Override
127 public T getCurrentNode() {
128 return node;
129 }
130
131 protected T traverse(T node) {
132 this.node = node;
133 doTraverse(node, this);
134 return this.node;
135 }
136 }
137
138 protected static void checkReplacement(@SuppressWarnings("UnusedParameters") JsNode origNode, JsNode newNode) {
139 if (newNode == null) throw new RuntimeException("Cannot replace with null");
140 }
141
142 @Override
143 protected <T extends JsNode> T doAccept(T node) {
144 return new NodeContext<T>().traverse(node);
145 }
146
147 @Override
148 protected JsExpression doAcceptLvalue(JsExpression expr) {
149 return new LvalueContext().traverse(expr);
150 }
151
152 @Override
153 protected <T extends JsStatement> JsStatement doAcceptStatement(T statement) {
154 List<JsStatement> statements = new SmartList<JsStatement>(statement);
155 doAcceptStatementList(statements);
156
157 if (statements.size() == 1) {
158 return statements.get(0);
159 }
160
161 return new JsBlock(statements);
162 }
163
164 @Override
165 protected void doAcceptStatementList(List<JsStatement> statements) {
166 ListContext<JsStatement> context = new ListContext<JsStatement>();
167 statementContexts.push(context);
168 context.traverse(statements);
169 statementContexts.pop();
170 }
171
172 @Override
173 protected <T extends JsNode> void doAcceptList(List<T> collection) {
174 new ListContext<T>().traverse(collection);
175 }
176
177 @NotNull
178 protected JsContext<JsStatement> getLastStatementLevelContext() {
179 return statementContexts.peek();
180 }
181
182 @Override
183 protected <T extends JsNode> void doTraverse(T node, JsContext ctx) {
184 node.traverse(this, ctx);
185 }
186
187 }