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, @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");
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 @NotNull
287 @Override
288 public PseudoValue newValue(@Nullable JetElement element) {
289 return valueFactory.newValue(element, null);
290 }
291
292 @Override
293 public void returnValue(@NotNull JetExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine) {
294 Label exitPoint = getExitPoint(subroutine);
295 handleJumpInsideTryFinally(exitPoint);
296 add(new ReturnValueInstruction(returnExpression, getCurrentScope(), exitPoint, returnValue));
297 }
298
299 @Override
300 public void returnNoValue(@NotNull JetReturnExpression returnExpression, @NotNull JetElement subroutine) {
301 Label exitPoint = getExitPoint(subroutine);
302 handleJumpInsideTryFinally(exitPoint);
303 add(new ReturnNoValueInstruction(returnExpression, getCurrentScope(), exitPoint));
304 }
305
306 @Override
307 public void write(
308 @NotNull JetElement assignment,
309 @NotNull JetElement lValue,
310 @NotNull PseudoValue rValue,
311 @NotNull AccessTarget target,
312 @NotNull Map<PseudoValue, ReceiverValue> receiverValues) {
313 add(new WriteValueInstruction(assignment, getCurrentScope(), target, receiverValues, lValue, rValue));
314 }
315
316 @Override
317 public void declareParameter(@NotNull JetParameter parameter) {
318 add(new VariableDeclarationInstruction(parameter, getCurrentScope()));
319 }
320
321 @Override
322 public void declareVariable(@NotNull JetVariableDeclaration property) {
323 add(new VariableDeclarationInstruction(property, getCurrentScope()));
324 }
325
326 @Override
327 public void declareFunction(@NotNull JetElement subroutine, @NotNull Pseudocode pseudocode) {
328 add(new LocalFunctionDeclarationInstruction(subroutine, pseudocode, getCurrentScope()));
329 }
330
331 @Override
332 public void loadUnit(@NotNull JetExpression expression) {
333 add(new LoadUnitValueInstruction(expression, getCurrentScope()));
334 }
335
336 @Override
337 public void jump(@NotNull Label label, @NotNull JetElement element) {
338 handleJumpInsideTryFinally(label);
339 add(new UnconditionalJumpInstruction(element, label, getCurrentScope()));
340 }
341
342 @Override
343 public void jumpOnFalse(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue conditionValue) {
344 handleJumpInsideTryFinally(label);
345 add(new ConditionalJumpInstruction(element, false, getCurrentScope(), label, conditionValue));
346 }
347
348 @Override
349 public void jumpOnTrue(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue conditionValue) {
350 handleJumpInsideTryFinally(label);
351 add(new ConditionalJumpInstruction(element, true, getCurrentScope(), label, conditionValue));
352 }
353
354 @Override
355 public void bindLabel(@NotNull Label label) {
356 pseudocode.bindLabel(label);
357 }
358
359 @Override
360 public void nondeterministicJump(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue inputValue) {
361 handleJumpInsideTryFinally(label);
362 add(new NondeterministicJumpInstruction(element, Collections.singletonList(label), getCurrentScope(), inputValue));
363 }
364
365 @Override
366 public void nondeterministicJump(@NotNull List<Label> labels, @NotNull JetElement element) {
367 //todo
368 //handleJumpInsideTryFinally(label);
369 add(new NondeterministicJumpInstruction(element, labels, getCurrentScope(), null));
370 }
371
372 @Override
373 public void jumpToError(@NotNull JetElement element) {
374 handleJumpInsideTryFinally(error);
375 add(new UnconditionalJumpInstruction(element, error, getCurrentScope()));
376 }
377
378 @Override
379 public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) {
380 allBlocks.push(new TryFinallyBlockInfo(generationTrigger));
381 }
382
383 @Override
384 public void throwException(@NotNull JetThrowExpression expression, @NotNull PseudoValue thrownValue) {
385 handleJumpInsideTryFinally(error);
386 add(new ThrowExceptionInstruction(expression, getCurrentScope(), error, thrownValue));
387 }
388
389 @Override
390 public void exitTryFinally() {
391 BlockInfo pop = allBlocks.pop();
392 assert pop instanceof TryFinallyBlockInfo;
393 }
394
395 @Override
396 public void repeatPseudocode(@NotNull Label startLabel, @NotNull Label finishLabel) {
397 pseudocode.repeatPart(startLabel, finishLabel);
398 }
399
400 @NotNull
401 @Override
402 public InstructionWithValue loadConstant(@NotNull JetExpression expression, @Nullable CompileTimeConstant<?> constant) {
403 return read(expression);
404 }
405
406 @NotNull
407 @Override
408 public InstructionWithValue createAnonymousObject(@NotNull JetObjectLiteralExpression expression) {
409 return read(expression);
410 }
411
412 @NotNull
413 @Override
414 public InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) {
415 return read(expression);
416 }
417
418 @NotNull
419 @Override
420 public InstructionWithValue loadStringTemplate(@NotNull JetStringTemplateExpression expression, @NotNull List<PseudoValue> inputValues) {
421 if (inputValues.isEmpty()) return read(expression);
422 Map<PseudoValue, TypePredicate> predicate = PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, inputValues);
423 return magic(expression, expression, inputValues, predicate, MagicKind.STRING_TEMPLATE);
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 @NotNull MagicKind kind
434 ) {
435 MagicInstruction instruction = MagicInstruction.OBJECT$.create(
436 instructionElement, valueElement, getCurrentScope(), inputValues, expectedTypes, kind, 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 JetExpression expression,
454 @NotNull ResolvedCall<?> resolvedCall,
455 @NotNull Map<PseudoValue, ReceiverValue> receiverValues
456 ) {
457 return read(expression, resolvedCall, receiverValues);
458 }
459
460 @NotNull
461 @Override
462 public CallInstruction call(
463 @NotNull JetElement valueElement,
464 @NotNull ResolvedCall<?> resolvedCall,
465 @NotNull Map<PseudoValue, ReceiverValue> receiverValues,
466 @NotNull Map<PseudoValue, ValueParameterDescriptor> arguments
467 ) {
468 JetType returnType = resolvedCall.getResultingDescriptor().getReturnType();
469 CallInstruction instruction = CallInstruction.OBJECT$.create(
470 valueElement,
471 getCurrentScope(),
472 resolvedCall,
473 receiverValues,
474 arguments,
475 returnType != null && KotlinBuiltIns.getInstance().isNothing(returnType) ? null : valueFactory
476 );
477 add(instruction);
478 return instruction;
479 }
480
481 @NotNull
482 @Override
483 public OperationInstruction predefinedOperation(
484 @NotNull JetExpression expression,
485 @NotNull PredefinedOperation operation,
486 @NotNull List<PseudoValue> inputValues
487 ) {
488 Map<PseudoValue, TypePredicate> expectedTypes;
489 switch(operation) {
490 case AND:
491 case OR:
492 SingleType onlyBoolean = new SingleType(KotlinBuiltIns.getInstance().getBooleanType());
493 expectedTypes = PseudocodePackage.expectedTypeFor(onlyBoolean, inputValues);
494 break;
495 case NOT_NULL_ASSERTION:
496 expectedTypes = PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, inputValues);
497 break;
498 default:
499 throw new IllegalArgumentException("Invalid operation: " + operation);
500 }
501
502 return magic(expression, expression, inputValues, expectedTypes, getMagicKind(operation));
503 }
504
505 @NotNull
506 private MagicKind getMagicKind(@NotNull PredefinedOperation operation) {
507 switch(operation) {
508 case AND:
509 return MagicKind.AND;
510 case OR:
511 return MagicKind.OR;
512 case NOT_NULL_ASSERTION:
513 return MagicKind.NOT_NULL_ASSERTION;
514 default:
515 throw new IllegalArgumentException("Invalid operation: " + operation);
516 }
517 }
518
519 @Override
520 public void compilationError(@NotNull JetElement element, @NotNull String message) {
521 add(new CompilationErrorInstruction(element, getCurrentScope(), message));
522 }
523
524 @NotNull
525 private ReadValueInstruction read(
526 @NotNull JetExpression expression,
527 @Nullable ResolvedCall<?> resolvedCall,
528 @NotNull Map<PseudoValue, ReceiverValue> receiverValues
529 ) {
530 AccessTarget accessTarget = resolvedCall != null ? new AccessTarget.Call(resolvedCall) : AccessTarget.BlackBox.INSTANCE$;
531 ReadValueInstruction instruction = ReadValueInstruction.OBJECT$.create(
532 expression, getCurrentScope(), accessTarget, receiverValues, valueFactory
533 );
534 add(instruction);
535 return instruction;
536 }
537
538 @NotNull
539 private ReadValueInstruction read(@NotNull JetExpression expression) {
540 return read(expression, null, Collections.<PseudoValue, ReceiverValue>emptyMap());
541 }
542 }
543
544 public static class TryFinallyBlockInfo extends BlockInfo {
545 private final GenerationTrigger finallyBlock;
546
547 private TryFinallyBlockInfo(GenerationTrigger finallyBlock) {
548 this.finallyBlock = finallyBlock;
549 }
550
551 public void generateFinallyBlock() {
552 finallyBlock.generate();
553 }
554 }
555
556 }