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    }