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