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.cfg.pseudocode.instructions.Instruction;
024    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.LexicalScope;
025    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.*;
026    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.*;
027    import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.*;
028    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
029    import org.jetbrains.jet.lang.psi.*;
030    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
031    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
032    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
033    import org.jetbrains.jet.lang.types.JetType;
034    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035    
036    import java.util.*;
037    
038    public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAdapter {
039        private JetControlFlowBuilder builder = null;
040    
041        private final Stack<BreakableBlockInfo> loopInfo = new Stack<BreakableBlockInfo>();
042        private final Stack<LexicalScope> lexicalScopes = new Stack<LexicalScope>();
043        private final Map<JetElement, BreakableBlockInfo> elementToBlockInfo = new HashMap<JetElement, BreakableBlockInfo>();
044        private int labelCount = 0;
045    
046        private final Stack<JetControlFlowInstructionsGeneratorWorker> builders = new Stack<JetControlFlowInstructionsGeneratorWorker>();
047    
048        private final Stack<BlockInfo> allBlocks = new Stack<BlockInfo>();
049    
050        @NotNull
051        @Override
052        protected JetControlFlowBuilder getDelegateBuilder() {
053            return builder;
054        }
055    
056        private void pushBuilder(JetElement scopingElement, JetElement subroutine) {
057            JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(scopingElement, subroutine);
058            builders.push(worker);
059            builder = worker;
060        }
061    
062        private JetControlFlowInstructionsGeneratorWorker popBuilder(@NotNull JetElement element) {
063            JetControlFlowInstructionsGeneratorWorker worker = builders.pop();
064            if (!builders.isEmpty()) {
065                builder = builders.peek();
066            }
067            else {
068                builder = null;
069            }
070            return worker;
071        }
072    
073        @Override
074        public void enterSubroutine(@NotNull JetElement subroutine) {
075            if (builder != null && subroutine instanceof JetFunctionLiteral) {
076                pushBuilder(subroutine, builder.getReturnSubroutine());
077            }
078            else {
079                pushBuilder(subroutine, subroutine);
080            }
081            assert builder != null;
082            builder.enterLexicalScope(subroutine);
083            builder.enterSubroutine(subroutine);
084        }
085    
086        @NotNull
087        @Override
088        public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
089            super.exitSubroutine(subroutine);
090            builder.exitLexicalScope(subroutine);
091            JetControlFlowInstructionsGeneratorWorker worker = popBuilder(subroutine);
092            if (!builders.empty()) {
093                JetControlFlowInstructionsGeneratorWorker builder = builders.peek();
094                builder.declareFunction(subroutine, worker.getPseudocode());
095            }
096            return worker.getPseudocode();
097        }
098    
099        private class JetControlFlowInstructionsGeneratorWorker implements JetControlFlowBuilder {
100    
101            private final PseudocodeImpl pseudocode;
102            private final Label error;
103            private final Label sink;
104            private final JetElement returnSubroutine;
105    
106            private final PseudoValueFactory valueFactory = new PseudoValueFactoryImpl() {
107                @NotNull
108                @Override
109                public PseudoValue newValue(@Nullable JetElement element, @NotNull InstructionWithValue instruction) {
110                    PseudoValue value = super.newValue(element, instruction);
111                    if (element != null) {
112                        bindValue(value, element);
113                    }
114                    return value;
115                }
116            };
117    
118            private JetControlFlowInstructionsGeneratorWorker(@NotNull JetElement scopingElement, @NotNull JetElement returnSubroutine) {
119                this.pseudocode = new PseudocodeImpl(scopingElement);
120                this.error = pseudocode.createLabel("error");
121                this.sink = pseudocode.createLabel("sink");
122                this.returnSubroutine = returnSubroutine;
123            }
124    
125            public PseudocodeImpl getPseudocode() {
126                return pseudocode;
127            }
128    
129            private void add(@NotNull Instruction instruction) {
130                pseudocode.addInstruction(instruction);
131            }
132    
133            @NotNull
134            @Override
135            public final Label createUnboundLabel() {
136                return pseudocode.createLabel("L" + labelCount++);
137            }
138    
139            @NotNull
140            @Override
141            public Label createUnboundLabel(@NotNull String name) {
142                return pseudocode.createLabel("L" + labelCount++ + " [" + name + "]");
143            }
144    
145            @Override
146            public final LoopInfo enterLoop(@NotNull JetExpression expression, @Nullable Label loopExitPoint, Label conditionEntryPoint) {
147                Label loopEntryLabel = createUnboundLabel("loop entry point");
148                bindLabel(loopEntryLabel);
149                LoopInfo blockInfo = new LoopInfo(
150                        expression,
151                        loopEntryLabel,
152                        loopExitPoint != null ? loopExitPoint : createUnboundLabel("loop exit point"),
153                        createUnboundLabel("body entry point"),
154                        conditionEntryPoint != null ? conditionEntryPoint : createUnboundLabel("condition entry point"));
155                loopInfo.push(blockInfo);
156                elementToBlockInfo.put(expression, blockInfo);
157                allBlocks.push(blockInfo);
158                pseudocode.recordLoopInfo(expression, blockInfo);
159                return blockInfo;
160            }
161    
162            @Override
163            public final void exitLoop(@NotNull JetExpression expression) {
164                BreakableBlockInfo info = loopInfo.pop();
165                elementToBlockInfo.remove(expression);
166                allBlocks.pop();
167                bindLabel(info.getExitPoint());
168            }
169    
170            @Override
171            public JetElement getCurrentLoop() {
172                return loopInfo.empty() ? null : loopInfo.peek().getElement();
173            }
174    
175            @Override
176            public void enterSubroutine(@NotNull JetElement subroutine) {
177                Label entryPoint = createUnboundLabel();
178                BreakableBlockInfo blockInfo = new BreakableBlockInfo(subroutine, entryPoint, createUnboundLabel());
179    //            subroutineInfo.push(blockInfo);
180                elementToBlockInfo.put(subroutine, blockInfo);
181                allBlocks.push(blockInfo);
182                bindLabel(entryPoint);
183                add(new SubroutineEnterInstruction(subroutine, getCurrentScope()));
184            }
185    
186            @NotNull
187            @Override
188            public JetElement getCurrentSubroutine() {
189                return pseudocode.getCorrespondingElement();
190            }
191    
192            @Override
193            public JetElement getReturnSubroutine() {
194                return returnSubroutine;// subroutineInfo.empty() ? null : subroutineInfo.peek().getElement();
195            }
196    
197            @NotNull
198            @Override
199            public Label getEntryPoint(@NotNull JetElement labelElement) {
200                return elementToBlockInfo.get(labelElement).getEntryPoint();
201            }
202    
203            @NotNull
204            @Override
205            public Label getExitPoint(@NotNull JetElement labelElement) {
206                BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement);
207                assert blockInfo != null : labelElement.getText();
208                return blockInfo.getExitPoint();
209            }
210    
211            @NotNull
212            private LexicalScope getCurrentScope() {
213                return lexicalScopes.peek();
214            }
215    
216            @Override
217            public void enterLexicalScope(@NotNull JetElement element) {
218                LexicalScope current = lexicalScopes.isEmpty() ? null : getCurrentScope();
219                LexicalScope scope = new LexicalScope(current, element);
220                lexicalScopes.push(scope);
221            }
222    
223            @Override
224            public void exitLexicalScope(@NotNull JetElement element) {
225                LexicalScope currentScope = getCurrentScope();
226                assert currentScope.getElement() == element : "Exit from not the current lexical scope.\n" +
227                        "Current scope is for: " + currentScope.getElement() + ".\n" +
228                        "Exit from the scope for: " + element.getText();
229                lexicalScopes.pop();
230            }
231    
232            ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
233    
234            private void handleJumpInsideTryFinally(Label jumpTarget) {
235                List<TryFinallyBlockInfo> finallyBlocks = new ArrayList<TryFinallyBlockInfo>();
236    
237                for (int i = allBlocks.size() - 1; i >= 0; i--) {
238                    BlockInfo blockInfo = allBlocks.get(i);
239                    if (blockInfo instanceof BreakableBlockInfo) {
240                        BreakableBlockInfo breakableBlockInfo = (BreakableBlockInfo) blockInfo;
241                        if (jumpTarget == breakableBlockInfo.getExitPoint() || jumpTarget == breakableBlockInfo.getEntryPoint()
242                            || jumpTarget == error) {
243                            for (int j = finallyBlocks.size() - 1; j >= 0; j--) {
244                                finallyBlocks.get(j).generateFinallyBlock();
245                            }
246                            break;
247                        }
248                    }
249                    else if (blockInfo instanceof TryFinallyBlockInfo) {
250                        TryFinallyBlockInfo tryFinallyBlockInfo = (TryFinallyBlockInfo) blockInfo;
251                        finallyBlocks.add(tryFinallyBlockInfo);
252                    }
253                }
254            }
255    
256            @NotNull
257            @Override
258            public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
259                bindLabel(getExitPoint(subroutine));
260                pseudocode.addExitInstruction(new SubroutineExitInstruction(subroutine, getCurrentScope(), false));
261                bindLabel(error);
262                pseudocode.addErrorInstruction(new SubroutineExitInstruction(subroutine, getCurrentScope(), true));
263                bindLabel(sink);
264                pseudocode.addSinkInstruction(new SubroutineSinkInstruction(subroutine, getCurrentScope(), "<SINK>"));
265                elementToBlockInfo.remove(subroutine);
266                allBlocks.pop();
267                return pseudocode;
268            }
269    
270            @Override
271            public void mark(@NotNull JetElement element) {
272                add(new MarkInstruction(element, getCurrentScope()));
273            }
274    
275            @Nullable
276            @Override
277            public PseudoValue getBoundValue(@Nullable JetElement element) {
278                return pseudocode.getElementValue(element);
279            }
280    
281            @Override
282            public void bindValue(@NotNull PseudoValue value, @NotNull JetElement element) {
283                pseudocode.bindElementToValue(element, value);
284            }
285    
286            @Override
287            public void returnValue(
288                    @NotNull JetReturnExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine
289            ) {
290                Label exitPoint = getExitPoint(subroutine);
291                handleJumpInsideTryFinally(exitPoint);
292                add(new ReturnValueInstruction(returnExpression, getCurrentScope(), exitPoint, returnValue));
293            }
294    
295            @Override
296            public void returnNoValue(@NotNull JetReturnExpression returnExpression, @NotNull JetElement subroutine) {
297                Label exitPoint = getExitPoint(subroutine);
298                handleJumpInsideTryFinally(exitPoint);
299                add(new ReturnNoValueInstruction(returnExpression, getCurrentScope(), exitPoint));
300            }
301    
302            @Override
303            public void write(
304                    @NotNull JetElement assignment,
305                    @NotNull JetElement lValue,
306                    @NotNull PseudoValue rValue,
307                    @NotNull AccessTarget target,
308                    @NotNull Map<PseudoValue, ReceiverValue> receiverValues) {
309                add(new WriteValueInstruction(assignment, getCurrentScope(), target, receiverValues, lValue, rValue));
310            }
311    
312            @Override
313            public void declareParameter(@NotNull JetParameter parameter) {
314                add(new VariableDeclarationInstruction(parameter, getCurrentScope()));
315            }
316    
317            @Override
318            public void declareVariable(@NotNull JetVariableDeclaration property) {
319                add(new VariableDeclarationInstruction(property, getCurrentScope()));
320            }
321    
322            @Override
323            public void declareFunction(@NotNull JetElement subroutine, @NotNull Pseudocode pseudocode) {
324                add(new LocalFunctionDeclarationInstruction(subroutine, pseudocode, getCurrentScope()));
325            }
326    
327            @Override
328            public void loadUnit(@NotNull JetExpression expression) {
329                add(new LoadUnitValueInstruction(expression, getCurrentScope()));
330            }
331    
332            @Override
333            public void jump(@NotNull Label label, @NotNull JetElement element) {
334                handleJumpInsideTryFinally(label);
335                add(new UnconditionalJumpInstruction(element, label, getCurrentScope()));
336            }
337    
338            @Override
339            public void jumpOnFalse(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue conditionValue) {
340                handleJumpInsideTryFinally(label);
341                add(new ConditionalJumpInstruction(element, false, getCurrentScope(), label, conditionValue));
342            }
343    
344            @Override
345            public void jumpOnTrue(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue conditionValue) {
346                handleJumpInsideTryFinally(label);
347                add(new ConditionalJumpInstruction(element, true, getCurrentScope(), label, conditionValue));
348            }
349    
350            @Override
351            public void bindLabel(@NotNull Label label) {
352                pseudocode.bindLabel(label);
353            }
354    
355            @Override
356            public void nondeterministicJump(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue inputValue) {
357                handleJumpInsideTryFinally(label);
358                add(new NondeterministicJumpInstruction(element, Collections.singletonList(label), getCurrentScope(), inputValue));
359            }
360    
361            @Override
362            public void nondeterministicJump(@NotNull List<Label> labels, @NotNull JetElement element) {
363                //todo
364                //handleJumpInsideTryFinally(label);
365                add(new NondeterministicJumpInstruction(element, labels, getCurrentScope(), null));
366            }
367    
368            @Override
369            public void jumpToError(@NotNull JetElement element) {
370                handleJumpInsideTryFinally(error);
371                add(new UnconditionalJumpInstruction(element, error, getCurrentScope()));
372            }
373    
374            @Override
375            public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) {
376                allBlocks.push(new TryFinallyBlockInfo(generationTrigger));
377            }
378    
379            @Override
380            public void throwException(@NotNull JetThrowExpression expression, @NotNull PseudoValue thrownValue) {
381                handleJumpInsideTryFinally(error);
382                add(new ThrowExceptionInstruction(expression, getCurrentScope(), error, thrownValue));
383            }
384    
385            @Override
386            public void exitTryFinally() {
387                BlockInfo pop = allBlocks.pop();
388                assert pop instanceof TryFinallyBlockInfo;
389            }
390    
391            @Override
392            public void unsupported(JetElement element) {
393                add(new UnsupportedElementInstruction(element, getCurrentScope()));
394            }
395    
396            @Override
397            public void repeatPseudocode(@NotNull Label startLabel, @NotNull Label finishLabel) {
398                pseudocode.repeatPart(startLabel, finishLabel);
399            }
400    
401            @NotNull
402            @Override
403            public InstructionWithValue loadConstant(@NotNull JetExpression expression, @Nullable CompileTimeConstant<?> constant) {
404                return read(expression);
405            }
406    
407            @NotNull
408            @Override
409            public InstructionWithValue createAnonymousObject(@NotNull JetObjectLiteralExpression expression) {
410                return read(expression);
411            }
412    
413            @NotNull
414            @Override
415            public InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) {
416                return read(expression);
417            }
418    
419            @NotNull
420            @Override
421            public InstructionWithValue loadStringTemplate(@NotNull JetStringTemplateExpression expression, @NotNull List<PseudoValue> inputValues) {
422                if (inputValues.isEmpty()) return read(expression);
423                return magic(expression, expression, inputValues, PseudocodePackage.expectedTypeFor(AllTypes.instance$, inputValues), false);
424            }
425    
426            @NotNull
427            @Override
428            public MagicInstruction magic(
429                    @NotNull JetElement instructionElement,
430                    @Nullable JetElement valueElement,
431                    @NotNull List<PseudoValue> inputValues,
432                    @NotNull Map<PseudoValue, TypePredicate> expectedTypes,
433                    boolean synthetic
434            ) {
435                MagicInstruction instruction = MagicInstruction.object$.create(
436                        instructionElement, valueElement, getCurrentScope(), synthetic, inputValues, expectedTypes, valueFactory
437                );
438                add(instruction);
439                return instruction;
440            }
441    
442            @NotNull
443            @Override
444            public MergeInstruction merge(@NotNull JetExpression expression, @NotNull List<PseudoValue> inputValues) {
445                MergeInstruction instruction = MergeInstruction.object$.create(expression, getCurrentScope(), inputValues, valueFactory);
446                add(instruction);
447                return instruction;
448            }
449    
450            @NotNull
451            @Override
452            public ReadValueInstruction readVariable(
453                    @NotNull JetElement instructionElement,
454                    @NotNull JetExpression valueElement,
455                    @NotNull ResolvedCall<?> resolvedCall,
456                    @NotNull Map<PseudoValue, ReceiverValue> receiverValues
457            ) {
458                return read(instructionElement, valueElement, resolvedCall, receiverValues);
459            }
460    
461            @NotNull
462            @Override
463            public CallInstruction call(
464                    @NotNull JetElement instructionElement,
465                    @Nullable JetExpression valueElement,
466                    @NotNull ResolvedCall<?> resolvedCall,
467                    @NotNull Map<PseudoValue, ReceiverValue> receiverValues,
468                    @NotNull Map<PseudoValue, ValueParameterDescriptor> arguments
469            ) {
470                JetType returnType = resolvedCall.getResultingDescriptor().getReturnType();
471                CallInstruction instruction = CallInstruction.object$.create(
472                        instructionElement,
473                        valueElement,
474                        getCurrentScope(),
475                        resolvedCall,
476                        receiverValues,
477                        arguments,
478                        returnType != null && KotlinBuiltIns.getInstance().isNothing(returnType) ? null : valueFactory
479                );
480                add(instruction);
481                return instruction;
482            }
483    
484            @NotNull
485            @Override
486            public OperationInstruction predefinedOperation(
487                    @NotNull JetExpression expression,
488                    @NotNull PredefinedOperation operation,
489                    @NotNull List<PseudoValue> inputValues
490            ) {
491                Map<PseudoValue, TypePredicate> expectedTypes;
492                switch(operation) {
493                    case AND:
494                    case OR:
495                        SingleType onlyBoolean = new SingleType(KotlinBuiltIns.getInstance().getBooleanType());
496                        expectedTypes = PseudocodePackage.expectedTypeFor(onlyBoolean, inputValues);
497                        break;
498                    case NOT_NULL_ASSERTION:
499                        expectedTypes = PseudocodePackage.expectedTypeFor(AllTypes.instance$, inputValues);
500                        break;
501                    default:
502                        throw new IllegalArgumentException("Invalid operation: " + operation);
503                }
504    
505                return magic(expression, expression, inputValues, expectedTypes, false);
506            }
507    
508            @Override
509            public void compilationError(@NotNull JetElement element, @NotNull String message) {
510                add(new CompilationErrorInstruction(element, getCurrentScope(), message));
511            }
512    
513            @NotNull
514            private ReadValueInstruction read(
515                    @NotNull JetElement instructionElement,
516                    @NotNull JetExpression expression,
517                    @Nullable ResolvedCall<?> resolvedCall,
518                    @NotNull Map<PseudoValue, ReceiverValue> receiverValues) {
519                AccessTarget accessTarget = resolvedCall != null ? new AccessTarget.Call(resolvedCall) : AccessTarget.BlackBox.instance$;
520                ReadValueInstruction instruction = ReadValueInstruction.object$.create(
521                        instructionElement, expression, getCurrentScope(), accessTarget, receiverValues, valueFactory
522                );
523                add(instruction);
524                return instruction;
525            }
526    
527            @NotNull
528            private ReadValueInstruction read(@NotNull JetExpression expression) {
529                return read(expression, expression, null, Collections.<PseudoValue, ReceiverValue>emptyMap());
530            }
531        }
532    
533        public static class TryFinallyBlockInfo extends BlockInfo {
534            private final GenerationTrigger finallyBlock;
535    
536            private TryFinallyBlockInfo(GenerationTrigger finallyBlock) {
537                this.finallyBlock = finallyBlock;
538            }
539    
540            public void generateFinallyBlock() {
541                finallyBlock.generate();
542            }
543        }
544    
545    }