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.descriptors.ReceiverParameterDescriptor;
024 import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
025 import org.jetbrains.jet.lang.psi.*;
026 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
027 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
028
029 import java.util.ArrayList;
030 import java.util.HashMap;
031 import java.util.List;
032 import java.util.Map;
033
034 public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAdapter {
035
036 private JetControlFlowBuilder builder = null;
037
038 private final Stack<BreakableBlockInfo> loopInfo = new Stack<BreakableBlockInfo>();
039 private final Stack<LexicalScope> lexicalScopes = new Stack<LexicalScope>();
040 private final Map<JetElement, BreakableBlockInfo> elementToBlockInfo = new HashMap<JetElement, BreakableBlockInfo>();
041 private int labelCount = 0;
042 private int allowDeadLabelCount = 0;
043
044 private final Stack<JetControlFlowInstructionsGeneratorWorker> builders = new Stack<JetControlFlowInstructionsGeneratorWorker>();
045
046 private final Stack<BlockInfo> allBlocks = new Stack<BlockInfo>();
047
048 @NotNull
049 @Override
050 protected JetControlFlowBuilder getDelegateBuilder() {
051 return builder;
052 }
053
054 private void pushBuilder(JetElement scopingElement, JetElement subroutine) {
055 JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(scopingElement, subroutine);
056 builders.push(worker);
057 builder = worker;
058 }
059
060 private JetControlFlowInstructionsGeneratorWorker popBuilder(@NotNull JetElement element) {
061 JetControlFlowInstructionsGeneratorWorker worker = builders.pop();
062 if (!builders.isEmpty()) {
063 builder = builders.peek();
064 }
065 else {
066 builder = null;
067 }
068 return worker;
069 }
070
071 @Override
072 public void enterSubroutine(@NotNull JetElement subroutine) {
073 if (builder != null && subroutine instanceof JetFunctionLiteral) {
074 pushBuilder(subroutine, builder.getReturnSubroutine());
075 }
076 else {
077 pushBuilder(subroutine, subroutine);
078 }
079 assert builder != null;
080 builder.enterLexicalScope(subroutine);
081 builder.enterSubroutine(subroutine);
082 }
083
084 @NotNull
085 @Override
086 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
087 super.exitSubroutine(subroutine);
088 builder.exitLexicalScope(subroutine);
089 JetControlFlowInstructionsGeneratorWorker worker = popBuilder(subroutine);
090 if (!builders.empty()) {
091 JetControlFlowInstructionsGeneratorWorker builder = builders.peek();
092 builder.declareFunction(subroutine, worker.getPseudocode());
093 }
094 return worker.getPseudocode();
095 }
096
097 private class JetControlFlowInstructionsGeneratorWorker implements JetControlFlowBuilder {
098
099 private final PseudocodeImpl pseudocode;
100 private final Label error;
101 private final Label sink;
102 private final JetElement returnSubroutine;
103
104 private JetControlFlowInstructionsGeneratorWorker(@NotNull JetElement scopingElement, @NotNull JetElement returnSubroutine) {
105 this.pseudocode = new PseudocodeImpl(scopingElement);
106 this.error = pseudocode.createLabel("error");
107 this.sink = pseudocode.createLabel("sink");
108 this.returnSubroutine = returnSubroutine;
109 }
110
111 public PseudocodeImpl getPseudocode() {
112 return pseudocode;
113 }
114
115 private void add(@NotNull Instruction instruction) {
116 pseudocode.addInstruction(instruction);
117 }
118
119 @NotNull
120 @Override
121 public final Label createUnboundLabel() {
122 return pseudocode.createLabel("L" + labelCount++);
123 }
124
125 @NotNull
126 @Override
127 public Label createUnboundLabel(@NotNull String name) {
128 return pseudocode.createLabel("L" + labelCount++ + " [" + name + "]");
129 }
130
131 @Override
132 public final LoopInfo enterLoop(@NotNull JetExpression expression, @Nullable Label loopExitPoint, Label conditionEntryPoint) {
133 Label loopEntryLabel = createUnboundLabel("loop entry point");
134 bindLabel(loopEntryLabel);
135 LoopInfo blockInfo = new LoopInfo(
136 expression,
137 loopEntryLabel,
138 loopExitPoint != null ? loopExitPoint : createUnboundLabel("loop exit point"),
139 createUnboundLabel("body entry point"),
140 conditionEntryPoint != null ? conditionEntryPoint : createUnboundLabel("condition entry point"));
141 loopInfo.push(blockInfo);
142 elementToBlockInfo.put(expression, blockInfo);
143 allBlocks.push(blockInfo);
144 pseudocode.recordLoopInfo(expression, blockInfo);
145 return blockInfo;
146 }
147
148 @Override
149 public final void exitLoop(@NotNull JetExpression expression) {
150 BreakableBlockInfo info = loopInfo.pop();
151 elementToBlockInfo.remove(expression);
152 allBlocks.pop();
153 bindLabel(info.getExitPoint());
154 }
155
156 @Override
157 public JetElement getCurrentLoop() {
158 return loopInfo.empty() ? null : loopInfo.peek().getElement();
159 }
160
161 @Override
162 public void enterSubroutine(@NotNull JetElement subroutine) {
163 Label entryPoint = createUnboundLabel();
164 BreakableBlockInfo blockInfo = new BreakableBlockInfo(subroutine, entryPoint, createUnboundLabel());
165 // subroutineInfo.push(blockInfo);
166 elementToBlockInfo.put(subroutine, blockInfo);
167 allBlocks.push(blockInfo);
168 bindLabel(entryPoint);
169 add(new SubroutineEnterInstruction(subroutine, getCurrentScope()));
170 }
171
172 @NotNull
173 @Override
174 public JetElement getCurrentSubroutine() {
175 return pseudocode.getCorrespondingElement();
176 }
177
178 @Override
179 public JetElement getReturnSubroutine() {
180 return returnSubroutine;// subroutineInfo.empty() ? null : subroutineInfo.peek().getElement();
181 }
182
183 @NotNull
184 @Override
185 public Label getEntryPoint(@NotNull JetElement labelElement) {
186 return elementToBlockInfo.get(labelElement).getEntryPoint();
187 }
188
189 @NotNull
190 @Override
191 public Label getExitPoint(@NotNull JetElement labelElement) {
192 BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement);
193 assert blockInfo != null : labelElement.getText();
194 return blockInfo.getExitPoint();
195 }
196
197 @NotNull
198 private LexicalScope getCurrentScope() {
199 return lexicalScopes.peek();
200 }
201
202 @Override
203 public void enterLexicalScope(@NotNull JetElement element) {
204 LexicalScope current = lexicalScopes.isEmpty() ? null : getCurrentScope();
205 LexicalScope scope = new LexicalScope(current, element);
206 lexicalScopes.push(scope);
207 }
208
209 @Override
210 public void exitLexicalScope(@NotNull JetElement element) {
211 LexicalScope currentScope = getCurrentScope();
212 assert currentScope.getElement() == element : "Exit from not the current lexical scope.\n" +
213 "Current scope is for: " + currentScope.getElement() + ".\n" +
214 "Exit from the scope for: " + element.getText();
215 lexicalScopes.pop();
216 }
217
218 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
219
220 private void handleJumpInsideTryFinally(Label jumpTarget) {
221 List<TryFinallyBlockInfo> finallyBlocks = new ArrayList<TryFinallyBlockInfo>();
222
223 for (int i = allBlocks.size() - 1; i >= 0; i--) {
224 BlockInfo blockInfo = allBlocks.get(i);
225 if (blockInfo instanceof BreakableBlockInfo) {
226 BreakableBlockInfo breakableBlockInfo = (BreakableBlockInfo) blockInfo;
227 if (jumpTarget == breakableBlockInfo.getExitPoint() || jumpTarget == breakableBlockInfo.getEntryPoint()
228 || jumpTarget == error) {
229 for (int j = finallyBlocks.size() - 1; j >= 0; j--) {
230 finallyBlocks.get(j).generateFinallyBlock();
231 }
232 break;
233 }
234 }
235 else if (blockInfo instanceof TryFinallyBlockInfo) {
236 TryFinallyBlockInfo tryFinallyBlockInfo = (TryFinallyBlockInfo) blockInfo;
237 finallyBlocks.add(tryFinallyBlockInfo);
238 }
239 }
240 }
241
242 @NotNull
243 @Override
244 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) {
245 bindLabel(getExitPoint(subroutine));
246 pseudocode.addExitInstruction(new SubroutineExitInstruction(subroutine, getCurrentScope(), false));
247 bindLabel(error);
248 pseudocode.addErrorInstruction(new SubroutineExitInstruction(subroutine, getCurrentScope(), true));
249 bindLabel(sink);
250 pseudocode.addSinkInstruction(new SubroutineSinkInstruction(subroutine, getCurrentScope(), "<SINK>"));
251 elementToBlockInfo.remove(subroutine);
252 allBlocks.pop();
253 return pseudocode;
254 }
255
256 @Override
257 public void mark(@NotNull JetElement element) {
258 add(new MarkInstruction(element, getCurrentScope()));
259 }
260
261 @Override
262 public void returnValue(@NotNull JetExpression returnExpression, @NotNull JetElement subroutine) {
263 Label exitPoint = getExitPoint(subroutine);
264 handleJumpInsideTryFinally(exitPoint);
265 add(new ReturnValueInstruction(returnExpression, getCurrentScope(), exitPoint));
266 }
267
268 @Override
269 public void returnNoValue(@NotNull JetElement returnExpression, @NotNull JetElement subroutine) {
270 Label exitPoint = getExitPoint(subroutine);
271 handleJumpInsideTryFinally(exitPoint);
272 add(new ReturnNoValueInstruction(returnExpression, getCurrentScope(), exitPoint));
273 }
274
275 @Override
276 public void write(@NotNull JetElement assignment, @NotNull JetElement lValue) {
277 add(new WriteValueInstruction(assignment, lValue, getCurrentScope()));
278 }
279
280 @Override
281 public void declareParameter(@NotNull JetParameter parameter) {
282 add(new VariableDeclarationInstruction(parameter, getCurrentScope()));
283 }
284
285 @Override
286 public void declareVariable(@NotNull JetVariableDeclaration property) {
287 add(new VariableDeclarationInstruction(property, getCurrentScope()));
288 }
289
290 @Override
291 public void declareFunction(@NotNull JetElement subroutine, @NotNull Pseudocode pseudocode) {
292 add(new LocalFunctionDeclarationInstruction(subroutine, pseudocode, getCurrentScope()));
293 }
294
295 @Override
296 public void loadUnit(@NotNull JetExpression expression) {
297 add(new LoadUnitValueInstruction(expression, getCurrentScope()));
298 }
299
300 @Override
301 public void jump(@NotNull Label label, @NotNull JetElement element) {
302 handleJumpInsideTryFinally(label);
303 add(new UnconditionalJumpInstruction(element, label, getCurrentScope()));
304 }
305
306 @Override
307 public void jumpOnFalse(@NotNull Label label, @NotNull JetElement element) {
308 handleJumpInsideTryFinally(label);
309 add(new ConditionalJumpInstruction(element, false, getCurrentScope(), label));
310 }
311
312 @Override
313 public void jumpOnTrue(@NotNull Label label, @NotNull JetElement element) {
314 handleJumpInsideTryFinally(label);
315 add(new ConditionalJumpInstruction(element, true, getCurrentScope(), label));
316 }
317
318 @Override
319 public void bindLabel(@NotNull Label label) {
320 pseudocode.bindLabel(label);
321 }
322
323 @Override
324 public void nondeterministicJump(@NotNull Label label, @NotNull JetElement element) {
325 handleJumpInsideTryFinally(label);
326 add(new NondeterministicJumpInstruction(element, label, getCurrentScope()));
327 }
328
329 @Override
330 public void nondeterministicJump(@NotNull List<Label> labels, @NotNull JetElement element) {
331 //todo
332 //handleJumpInsideTryFinally(label);
333 add(new NondeterministicJumpInstruction(element, labels, getCurrentScope()));
334 }
335
336 @Override
337 public void jumpToError(@NotNull JetElement element) {
338 handleJumpInsideTryFinally(error);
339 add(new UnconditionalJumpInstruction(element, error, getCurrentScope()));
340 }
341
342 @Override
343 public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) {
344 allBlocks.push(new TryFinallyBlockInfo(generationTrigger));
345 }
346
347 @Override
348 public void throwException(@NotNull JetThrowExpression expression) {
349 handleJumpInsideTryFinally(error);
350 add(new ThrowExceptionInstruction(expression, getCurrentScope(), error));
351 }
352
353 @Override
354 public void exitTryFinally() {
355 BlockInfo pop = allBlocks.pop();
356 assert pop instanceof TryFinallyBlockInfo;
357 }
358
359 @Override
360 public void unsupported(JetElement element) {
361 add(new UnsupportedElementInstruction(element, getCurrentScope()));
362 }
363
364 @Override
365 public void repeatPseudocode(@NotNull Label startLabel, @NotNull Label finishLabel) {
366 pseudocode.repeatPart(startLabel, finishLabel);
367 }
368
369 @Override
370 public void loadConstant(@NotNull JetExpression expression, @Nullable CompileTimeConstant<?> constant) {
371 read(expression);
372 }
373
374 @Override
375 public void createAnonymousObject(@NotNull JetObjectLiteralExpression expression) {
376 read(expression);
377 }
378
379 @Override
380 public void createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) {
381 read(expression);
382 }
383
384 @Override
385 public void loadStringTemplate(@NotNull JetStringTemplateExpression expression) {
386 read(expression);
387 }
388
389 @Override
390 public void readThis(@NotNull JetExpression expression, @Nullable ReceiverParameterDescriptor parameterDescriptor) {
391 read(expression);
392 }
393
394 @Override
395 public void readVariable(@NotNull JetExpression expression, @Nullable VariableDescriptor variableDescriptor) {
396 read(expression);
397 }
398
399 @Override
400 public void call(@NotNull JetExpression expression, @NotNull ResolvedCall<?> resolvedCall) {
401 add(new CallInstruction(expression, getCurrentScope(), resolvedCall));
402 }
403
404 @Override
405 public void predefinedOperation(@NotNull JetExpression expression, @Nullable PredefinedOperation operation) {
406 read(expression);
407 }
408
409 @Override
410 public void compilationError(@NotNull JetElement element, @NotNull String message) {
411 add(new CompilationErrorInstruction(element, getCurrentScope(), message));
412 }
413
414 private void read(@NotNull JetElement element) {
415 add(new ReadValueInstruction(element, getCurrentScope()));
416 }
417 }
418
419 public static class TryFinallyBlockInfo extends BlockInfo {
420 private final GenerationTrigger finallyBlock;
421
422 private TryFinallyBlockInfo(GenerationTrigger finallyBlock) {
423 this.finallyBlock = finallyBlock;
424 }
425
426 public void generateFinallyBlock() {
427 finallyBlock.generate();
428 }
429 }
430
431 }