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
017package org.jetbrains.jet.lang.cfg.pseudocode;
018
019import com.intellij.util.containers.Stack;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.cfg.*;
023import org.jetbrains.jet.lang.psi.*;
024
025import java.util.ArrayList;
026import java.util.HashMap;
027import java.util.List;
028import java.util.Map;
029
030public 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}