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