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 }