001 /*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package org.jetbrains.jet.lang.cfg.pseudocode;
018
019 import com.intellij.util.containers.Stack;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.lang.cfg.*;
023 import org.jetbrains.jet.lang.psi.*;
024
025 import java.util.ArrayList;
026 import java.util.HashMap;
027 import java.util.List;
028 import java.util.Map;
029
030 public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAdapter {
031
032 private final Stack<BreakableBlockInfo> loopInfo = new Stack<BreakableBlockInfo>();
033 private final Map<JetElement, BreakableBlockInfo> elementToBlockInfo = new HashMap<JetElement, BreakableBlockInfo>();
034 private int labelCount = 0;
035 private int allowDeadLabelCount = 0;
036
037 private final Stack<JetControlFlowInstructionsGeneratorWorker> builders = new Stack<JetControlFlowInstructionsGeneratorWorker>();
038
039 private final Stack<BlockInfo> allBlocks = new Stack<BlockInfo>();
040
041 private void pushBuilder(JetElement scopingElement, JetElement subroutine) {
042 JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(scopingElement, subroutine);
043 builders.push(worker);
044 builder = worker;
045 }
046
047 private JetControlFlowInstructionsGeneratorWorker popBuilder(@NotNull JetElement element) {
048 JetControlFlowInstructionsGeneratorWorker worker = builders.pop();
049 if (!builders.isEmpty()) {
050 builder = builders.peek();
051 }
052 else {
053 builder = null;
054 }
055 return worker;
056 }
057
058 @Override
059 public void enterSubroutine(@NotNull JetElement subroutine) {
060 if (builder != null && subroutine instanceof JetFunctionLiteral) {
061 pushBuilder(subroutine, builder.getReturnSubroutine());
062 }
063 else {
064 pushBuilder(subroutine, subroutine);
065 }
066 assert builder != null;
067 builder.enterSubroutine(subroutine);
068 }
069
070 @Override
071 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
072 super.exitSubroutine(subroutine);
073 JetControlFlowInstructionsGeneratorWorker worker = popBuilder(subroutine);
074 if (!builders.empty()) {
075 JetControlFlowInstructionsGeneratorWorker builder = builders.peek();
076 LocalDeclarationInstruction instruction = new LocalDeclarationInstruction(subroutine, worker.getPseudocode());
077 builder.add(instruction);
078 }
079 return worker.getPseudocode();
080 }
081
082 private class JetControlFlowInstructionsGeneratorWorker implements JetControlFlowBuilder {
083
084 private final PseudocodeImpl pseudocode;
085 private final Label error;
086 private final Label sink;
087 private final JetElement returnSubroutine;
088
089 private JetControlFlowInstructionsGeneratorWorker(@NotNull JetElement scopingElement, @NotNull JetElement returnSubroutine) {
090 this.pseudocode = new PseudocodeImpl(scopingElement);
091 this.error = pseudocode.createLabel("error");
092 this.sink = pseudocode.createLabel("sink");
093 this.returnSubroutine = returnSubroutine;
094 }
095
096 public PseudocodeImpl getPseudocode() {
097 return pseudocode;
098 }
099
100 private void add(@NotNull Instruction instruction) {
101 pseudocode.addInstruction(instruction);
102 }
103
104 @NotNull
105 @Override
106 public final Label createUnboundLabel() {
107 return pseudocode.createLabel("L" + labelCount++);
108 }
109
110 @NotNull
111 @Override
112 public Label createUnboundLabel(@NotNull String name) {
113 return pseudocode.createLabel("L" + labelCount++ + " [" + name + "]");
114 }
115
116 @Override
117 public final LoopInfo enterLoop(@NotNull JetExpression expression, @Nullable Label loopExitPoint, Label conditionEntryPoint) {
118 Label loopEntryLabel = createUnboundLabel("loop entry point");
119 bindLabel(loopEntryLabel);
120 LoopInfo blockInfo = new LoopInfo(
121 expression,
122 loopEntryLabel,
123 loopExitPoint != null ? loopExitPoint : createUnboundLabel("loop exit point"),
124 createUnboundLabel("body entry point"),
125 conditionEntryPoint != null ? conditionEntryPoint : createUnboundLabel("condition entry point"));
126 loopInfo.push(blockInfo);
127 elementToBlockInfo.put(expression, blockInfo);
128 allBlocks.push(blockInfo);
129 pseudocode.recordLoopInfo(expression, blockInfo);
130 return blockInfo;
131 }
132
133 @Override
134 public final void exitLoop(@NotNull JetExpression expression) {
135 BreakableBlockInfo info = loopInfo.pop();
136 elementToBlockInfo.remove(expression);
137 allBlocks.pop();
138 bindLabel(info.getExitPoint());
139 }
140
141 @Override
142 public JetElement getCurrentLoop() {
143 return loopInfo.empty() ? null : loopInfo.peek().getElement();
144 }
145
146 @Override
147 public void enterSubroutine(@NotNull JetElement subroutine) {
148 Label entryPoint = createUnboundLabel();
149 BreakableBlockInfo blockInfo = new BreakableBlockInfo(subroutine, entryPoint, createUnboundLabel());
150 // subroutineInfo.push(blockInfo);
151 elementToBlockInfo.put(subroutine, blockInfo);
152 allBlocks.push(blockInfo);
153 bindLabel(entryPoint);
154 add(new SubroutineEnterInstruction(subroutine));
155 }
156
157 @NotNull
158 @Override
159 public JetElement getCurrentSubroutine() {
160 return pseudocode.getCorrespondingElement();
161 }
162
163 @Override
164 public JetElement getReturnSubroutine() {
165 return returnSubroutine;// subroutineInfo.empty() ? null : subroutineInfo.peek().getElement();
166 }
167
168 @Override
169 public Label getEntryPoint(@NotNull JetElement labelElement) {
170 return elementToBlockInfo.get(labelElement).getEntryPoint();
171 }
172
173 @Override
174 public Label getExitPoint(@NotNull JetElement labelElement) {
175 BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement);
176 assert blockInfo != null : labelElement.getText();
177 return blockInfo.getExitPoint();
178 }
179
180 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
181
182 private void handleJumpInsideTryFinally(Label jumpTarget) {
183 List<TryFinallyBlockInfo> finallyBlocks = new ArrayList<TryFinallyBlockInfo>();
184
185 for (int i = allBlocks.size() - 1; i >= 0; i--) {
186 BlockInfo blockInfo = allBlocks.get(i);
187 if (blockInfo instanceof BreakableBlockInfo) {
188 BreakableBlockInfo breakableBlockInfo = (BreakableBlockInfo) blockInfo;
189 if (jumpTarget == breakableBlockInfo.getExitPoint() || jumpTarget == breakableBlockInfo.getEntryPoint()
190 || jumpTarget == error) {
191 for (int j = finallyBlocks.size() - 1; j >= 0; j--) {
192 finallyBlocks.get(j).generateFinallyBlock();
193 }
194 break;
195 }
196 }
197 else if (blockInfo instanceof TryFinallyBlockInfo) {
198 TryFinallyBlockInfo tryFinallyBlockInfo = (TryFinallyBlockInfo) blockInfo;
199 finallyBlocks.add(tryFinallyBlockInfo);
200 }
201 }
202 }
203
204 @Override
205 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
206 bindLabel(getExitPoint(subroutine));
207 pseudocode.addExitInstruction(new SubroutineExitInstruction(subroutine, "<END>"));
208 bindLabel(error);
209 pseudocode.addErrorInstruction(new SubroutineExitInstruction(subroutine, "<ERROR>"));
210 bindLabel(sink);
211 pseudocode.addSinkInstruction(new SubroutineSinkInstruction(subroutine, "<SINK>"));
212 elementToBlockInfo.remove(subroutine);
213 allBlocks.pop();
214 return null;
215 }
216
217 @Override
218 public void returnValue(@NotNull JetExpression returnExpression, @NotNull JetElement subroutine) {
219 Label exitPoint = getExitPoint(subroutine);
220 handleJumpInsideTryFinally(exitPoint);
221 add(new ReturnValueInstruction(returnExpression, exitPoint));
222 }
223
224 @Override
225 public void returnNoValue(@NotNull JetElement returnExpression, @NotNull JetElement subroutine) {
226 Label exitPoint = getExitPoint(subroutine);
227 handleJumpInsideTryFinally(exitPoint);
228 add(new ReturnNoValueInstruction(returnExpression, exitPoint));
229 }
230
231 @Override
232 public void write(@NotNull JetElement assignment, @NotNull JetElement lValue) {
233 add(new WriteValueInstruction(assignment, lValue));
234 }
235
236 @Override
237 public void declare(@NotNull JetParameter parameter) {
238 add(new VariableDeclarationInstruction(parameter));
239 }
240
241 @Override
242 public void declare(@NotNull JetVariableDeclaration property) {
243 add(new VariableDeclarationInstruction(property));
244 }
245
246 @Override
247 public void read(@NotNull JetElement element) {
248 add(new ReadValueInstruction(element));
249 }
250
251 @Override
252 public void readUnit(@NotNull JetExpression expression) {
253 add(new ReadUnitValueInstruction(expression));
254 }
255
256 @Override
257 public void jump(@NotNull Label label) {
258 handleJumpInsideTryFinally(label);
259 add(new UnconditionalJumpInstruction(label));
260 }
261
262 @Override
263 public void jumpOnFalse(@NotNull Label label) {
264 handleJumpInsideTryFinally(label);
265 add(new ConditionalJumpInstruction(false, label));
266 }
267
268 @Override
269 public void jumpOnTrue(@NotNull Label label) {
270 handleJumpInsideTryFinally(label);
271 add(new ConditionalJumpInstruction(true, label));
272 }
273
274 @Override
275 public void bindLabel(@NotNull Label label) {
276 pseudocode.bindLabel(label);
277 }
278
279 @Override
280 public void nondeterministicJump(Label label) {
281 handleJumpInsideTryFinally(label);
282 add(new NondeterministicJumpInstruction(label));
283 }
284
285 @Override
286 public void nondeterministicJump(List<Label> labels) {
287 //todo
288 //handleJumpInsideTryFinally(label);
289 add(new NondeterministicJumpInstruction(labels));
290 }
291
292 @Override
293 public void jumpToError() {
294 handleJumpInsideTryFinally(error);
295 add(new UnconditionalJumpInstruction(error));
296 }
297
298 @Override
299 public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) {
300 allBlocks.push(new TryFinallyBlockInfo(generationTrigger));
301 }
302
303 @Override
304 public void throwException(@NotNull JetThrowExpression expression) {
305 handleJumpInsideTryFinally(error);
306 add(new ThrowExceptionInstruction(expression, error));
307 }
308
309 public void exitTryFinally() {
310 BlockInfo pop = allBlocks.pop();
311 assert pop instanceof TryFinallyBlockInfo;
312 }
313
314 @Override
315 public void unsupported(JetElement element) {
316 add(new UnsupportedElementInstruction(element));
317 }
318
319 @Override
320 public void repeatPseudocode(@NotNull Label startLabel, @NotNull Label finishLabel) {
321 pseudocode.repeatPart(startLabel, finishLabel);
322 }
323 }
324
325 public static class TryFinallyBlockInfo extends BlockInfo {
326 private final GenerationTrigger finallyBlock;
327
328 private TryFinallyBlockInfo(GenerationTrigger finallyBlock) {
329 this.finallyBlock = finallyBlock;
330 }
331
332 public void generateFinallyBlock() {
333 finallyBlock.generate();
334 }
335 }
336
337 }