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.descriptors.ReceiverParameterDescriptor;
024    import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
025    import org.jetbrains.jet.lang.psi.*;
026    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
027    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
028    
029    import java.util.ArrayList;
030    import java.util.HashMap;
031    import java.util.List;
032    import java.util.Map;
033    
034    public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAdapter {
035    
036        private JetControlFlowBuilder builder = null;
037    
038        private final Stack<BreakableBlockInfo> loopInfo = new Stack<BreakableBlockInfo>();
039        private final Map<JetElement, BreakableBlockInfo> elementToBlockInfo = new HashMap<JetElement, BreakableBlockInfo>();
040        private int labelCount = 0;
041        private int allowDeadLabelCount = 0;
042    
043        private final Stack<JetControlFlowInstructionsGeneratorWorker> builders = new Stack<JetControlFlowInstructionsGeneratorWorker>();
044    
045        private final Stack<BlockInfo> allBlocks = new Stack<BlockInfo>();
046    
047        @NotNull
048        @Override
049        protected JetControlFlowBuilder getDelegateBuilder() {
050            return builder;
051        }
052    
053        private void pushBuilder(JetElement scopingElement, JetElement subroutine) {
054            JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(scopingElement, subroutine);
055            builders.push(worker);
056            builder = worker;
057        }
058    
059        private JetControlFlowInstructionsGeneratorWorker popBuilder(@NotNull JetElement element) {
060            JetControlFlowInstructionsGeneratorWorker worker = builders.pop();
061            if (!builders.isEmpty()) {
062                builder = builders.peek();
063            }
064            else {
065                builder = null;
066            }
067            return worker;
068        }
069    
070        @Override
071        public void enterSubroutine(@NotNull JetElement subroutine) {
072            if (builder != null && subroutine instanceof JetFunctionLiteral) {
073                pushBuilder(subroutine, builder.getReturnSubroutine());
074            }
075            else {
076                pushBuilder(subroutine, subroutine);
077            }
078            assert builder != null;
079            builder.enterSubroutine(subroutine);
080        }
081    
082        @NotNull
083        @Override
084        public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
085            super.exitSubroutine(subroutine);
086            JetControlFlowInstructionsGeneratorWorker worker = popBuilder(subroutine);
087            if (!builders.empty()) {
088                JetControlFlowInstructionsGeneratorWorker builder = builders.peek();
089                builder.declareFunction(subroutine, worker.getPseudocode());
090            }
091            return worker.getPseudocode();
092        }
093    
094        private class JetControlFlowInstructionsGeneratorWorker implements JetControlFlowBuilder {
095    
096            private final PseudocodeImpl pseudocode;
097            private final Label error;
098            private final Label sink;
099            private final JetElement returnSubroutine;
100    
101            private JetControlFlowInstructionsGeneratorWorker(@NotNull JetElement scopingElement, @NotNull JetElement returnSubroutine) {
102                this.pseudocode = new PseudocodeImpl(scopingElement);
103                this.error = pseudocode.createLabel("error");
104                this.sink = pseudocode.createLabel("sink");
105                this.returnSubroutine = returnSubroutine;
106            }
107    
108            public PseudocodeImpl getPseudocode() {
109                return pseudocode;
110            }
111    
112            private void add(@NotNull Instruction instruction) {
113                pseudocode.addInstruction(instruction);
114            }
115    
116            @NotNull
117            @Override
118            public final Label createUnboundLabel() {
119                return pseudocode.createLabel("L" + labelCount++);
120            }
121    
122            @NotNull
123            @Override
124            public Label createUnboundLabel(@NotNull String name) {
125                return pseudocode.createLabel("L" + labelCount++ + " [" + name + "]");
126            }
127    
128            @Override
129            public final LoopInfo enterLoop(@NotNull JetExpression expression, @Nullable Label loopExitPoint, Label conditionEntryPoint) {
130                Label loopEntryLabel = createUnboundLabel("loop entry point");
131                bindLabel(loopEntryLabel);
132                LoopInfo blockInfo = new LoopInfo(
133                        expression,
134                        loopEntryLabel,
135                        loopExitPoint != null ? loopExitPoint : createUnboundLabel("loop exit point"),
136                        createUnboundLabel("body entry point"),
137                        conditionEntryPoint != null ? conditionEntryPoint : createUnboundLabel("condition entry point"));
138                loopInfo.push(blockInfo);
139                elementToBlockInfo.put(expression, blockInfo);
140                allBlocks.push(blockInfo);
141                pseudocode.recordLoopInfo(expression, blockInfo);
142                return blockInfo;
143            }
144    
145            @Override
146            public final void exitLoop(@NotNull JetExpression expression) {
147                BreakableBlockInfo info = loopInfo.pop();
148                elementToBlockInfo.remove(expression);
149                allBlocks.pop();
150                bindLabel(info.getExitPoint());
151            }
152    
153            @Override
154            public JetElement getCurrentLoop() {
155                return loopInfo.empty() ? null : loopInfo.peek().getElement();
156            }
157    
158            @Override
159            public void enterSubroutine(@NotNull JetElement subroutine) {
160                Label entryPoint = createUnboundLabel();
161                BreakableBlockInfo blockInfo = new BreakableBlockInfo(subroutine, entryPoint, createUnboundLabel());
162    //            subroutineInfo.push(blockInfo);
163                elementToBlockInfo.put(subroutine, blockInfo);
164                allBlocks.push(blockInfo);
165                bindLabel(entryPoint);
166                add(new SubroutineEnterInstruction(subroutine));
167            }
168    
169            @NotNull
170            @Override
171            public JetElement getCurrentSubroutine() {
172                return pseudocode.getCorrespondingElement();
173            }
174    
175            @Override
176            public JetElement getReturnSubroutine() {
177                return returnSubroutine;// subroutineInfo.empty() ? null : subroutineInfo.peek().getElement();
178            }
179    
180            @NotNull
181            @Override
182            public Label getEntryPoint(@NotNull JetElement labelElement) {
183                return elementToBlockInfo.get(labelElement).getEntryPoint();
184            }
185    
186            @NotNull
187            @Override
188            public Label getExitPoint(@NotNull JetElement labelElement) {
189                BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement);
190                assert blockInfo != null : labelElement.getText();
191                return blockInfo.getExitPoint();
192            }
193    
194    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
195    
196            private void handleJumpInsideTryFinally(Label jumpTarget) {
197                List<TryFinallyBlockInfo> finallyBlocks = new ArrayList<TryFinallyBlockInfo>();
198    
199                for (int i = allBlocks.size() - 1; i >= 0; i--) {
200                    BlockInfo blockInfo = allBlocks.get(i);
201                    if (blockInfo instanceof BreakableBlockInfo) {
202                        BreakableBlockInfo breakableBlockInfo = (BreakableBlockInfo) blockInfo;
203                        if (jumpTarget == breakableBlockInfo.getExitPoint() || jumpTarget == breakableBlockInfo.getEntryPoint()
204                            || jumpTarget == error) {
205                            for (int j = finallyBlocks.size() - 1; j >= 0; j--) {
206                                finallyBlocks.get(j).generateFinallyBlock();
207                            }
208                            break;
209                        }
210                    }
211                    else if (blockInfo instanceof TryFinallyBlockInfo) {
212                        TryFinallyBlockInfo tryFinallyBlockInfo = (TryFinallyBlockInfo) blockInfo;
213                        finallyBlocks.add(tryFinallyBlockInfo);
214                    }
215                }
216            }
217    
218            @NotNull
219            @Override
220            public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
221                bindLabel(getExitPoint(subroutine));
222                pseudocode.addExitInstruction(new SubroutineExitInstruction(subroutine, false));
223                bindLabel(error);
224                pseudocode.addErrorInstruction(new SubroutineExitInstruction(subroutine, true));
225                bindLabel(sink);
226                pseudocode.addSinkInstruction(new SubroutineSinkInstruction(subroutine, "<SINK>"));
227                elementToBlockInfo.remove(subroutine);
228                allBlocks.pop();
229                return pseudocode;
230            }
231    
232            @Override
233            public void mark(@NotNull JetElement element) {
234                add(new MarkInstruction(element));
235            }
236    
237            @Override
238            public void returnValue(@NotNull JetExpression returnExpression, @NotNull JetElement subroutine) {
239                Label exitPoint = getExitPoint(subroutine);
240                handleJumpInsideTryFinally(exitPoint);
241                add(new ReturnValueInstruction(returnExpression, exitPoint));
242            }
243    
244            @Override
245            public void returnNoValue(@NotNull JetElement returnExpression, @NotNull JetElement subroutine) {
246                Label exitPoint = getExitPoint(subroutine);
247                handleJumpInsideTryFinally(exitPoint);
248                add(new ReturnNoValueInstruction(returnExpression, exitPoint));
249            }
250    
251            @Override
252            public void write(@NotNull JetElement assignment, @NotNull JetElement lValue) {
253                add(new WriteValueInstruction(assignment, lValue));
254            }
255    
256            @Override
257            public void declareParameter(@NotNull JetParameter parameter) {
258                add(new VariableDeclarationInstruction(parameter));
259            }
260    
261            @Override
262            public void declareVariable(@NotNull JetVariableDeclaration property) {
263                add(new VariableDeclarationInstruction(property));
264            }
265    
266            @Override
267            public void declareFunction(@NotNull JetElement subroutine, @NotNull Pseudocode pseudocode) {
268                add(new LocalFunctionDeclarationInstruction(subroutine, pseudocode));
269            }
270    
271            @Override
272            public void loadUnit(@NotNull JetExpression expression) {
273                add(new LoadUnitValueInstruction(expression));
274            }
275    
276            @Override
277            public void jump(@NotNull Label label) {
278                handleJumpInsideTryFinally(label);
279                add(new UnconditionalJumpInstruction(label));
280            }
281    
282            @Override
283            public void jumpOnFalse(@NotNull Label label) {
284                handleJumpInsideTryFinally(label);
285                add(new ConditionalJumpInstruction(false, label));
286            }
287    
288            @Override
289            public void jumpOnTrue(@NotNull Label label) {
290                handleJumpInsideTryFinally(label);
291                add(new ConditionalJumpInstruction(true, label));
292            }
293    
294            @Override
295            public void bindLabel(@NotNull Label label) {
296                pseudocode.bindLabel(label);
297            }
298    
299            @Override
300            public void nondeterministicJump(@NotNull Label label) {
301                handleJumpInsideTryFinally(label);
302                add(new NondeterministicJumpInstruction(label));
303            }
304    
305            @Override
306            public void nondeterministicJump(@NotNull List<Label> labels) {
307                //todo
308                //handleJumpInsideTryFinally(label);
309                add(new NondeterministicJumpInstruction(labels));
310            }
311    
312            @Override
313            public void jumpToError() {
314                handleJumpInsideTryFinally(error);
315                add(new UnconditionalJumpInstruction(error));
316            }
317    
318            @Override
319            public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) {
320                allBlocks.push(new TryFinallyBlockInfo(generationTrigger));
321            }
322    
323            @Override
324            public void throwException(@NotNull JetThrowExpression expression) {
325                handleJumpInsideTryFinally(error);
326                add(new ThrowExceptionInstruction(expression, error));
327            }
328    
329            public void exitTryFinally() {
330                BlockInfo pop = allBlocks.pop();
331                assert pop instanceof TryFinallyBlockInfo;
332            }
333    
334            @Override
335            public void unsupported(JetElement element) {
336                add(new UnsupportedElementInstruction(element));
337            }
338    
339            @Override
340            public void repeatPseudocode(@NotNull Label startLabel, @NotNull Label finishLabel) {
341                pseudocode.repeatPart(startLabel, finishLabel);
342            }
343    
344            @Override
345            public void loadConstant(@NotNull JetExpression expression, @Nullable CompileTimeConstant<?> constant) {
346                read(expression);
347            }
348    
349            @Override
350            public void createAnonymousObject(@NotNull JetObjectLiteralExpression expression) {
351                read(expression);
352            }
353    
354            @Override
355            public void createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) {
356                read(expression);
357            }
358    
359            @Override
360            public void loadStringTemplate(@NotNull JetStringTemplateExpression expression) {
361                read(expression);
362            }
363    
364            @Override
365            public void readThis(@NotNull JetExpression expression, @Nullable ReceiverParameterDescriptor parameterDescriptor) {
366                read(expression);
367            }
368    
369            @Override
370            public void readVariable(@NotNull JetExpression expression, @Nullable VariableDescriptor variableDescriptor) {
371                read(expression);
372            }
373    
374            @Override
375            public void call(@NotNull JetExpression expression, @NotNull ResolvedCall<?> resolvedCall) {
376                add(new CallInstruction(expression, resolvedCall));
377            }
378    
379            @Override
380            public void predefinedOperation(@NotNull JetExpression expression, @Nullable PredefinedOperation operation) {
381                read(expression);
382            }
383    
384            @Override
385            public void compilationError(@NotNull JetElement element, @NotNull String message) {
386                add(new CompilationErrorInstruction(element, message));
387            }
388    
389            private void read(@NotNull JetElement element) {
390                add(new ReadValueInstruction(element));
391            }
392        }
393    
394        public static class TryFinallyBlockInfo extends BlockInfo {
395            private final GenerationTrigger finallyBlock;
396    
397            private TryFinallyBlockInfo(GenerationTrigger finallyBlock) {
398                this.finallyBlock = finallyBlock;
399            }
400    
401            public void generateFinallyBlock() {
402                finallyBlock.generate();
403            }
404        }
405    
406    }