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 }