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 017package org.jetbrains.jet.lang.cfg.pseudocode; 018 019import com.intellij.util.containers.Stack; 020import org.jetbrains.annotations.NotNull; 021import org.jetbrains.annotations.Nullable; 022import org.jetbrains.jet.lang.cfg.*; 023import org.jetbrains.jet.lang.psi.*; 024 025import java.util.ArrayList; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAdapter { 031 032 private final Stack<BreakableBlockInfo> loopInfo = new Stack<BreakableBlockInfo>(); 033 private final Map<JetElement, BreakableBlockInfo> elementToBlockInfo = new HashMap<JetElement, BreakableBlockInfo>(); 034 private int labelCount = 0; 035 private int allowDeadLabelCount = 0; 036 037 private final Stack<JetControlFlowInstructionsGeneratorWorker> builders = new Stack<JetControlFlowInstructionsGeneratorWorker>(); 038 039 private final Stack<BlockInfo> allBlocks = new Stack<BlockInfo>(); 040 041 private void pushBuilder(JetElement scopingElement, JetElement subroutine) { 042 JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(scopingElement, subroutine); 043 builders.push(worker); 044 builder = worker; 045 } 046 047 private JetControlFlowInstructionsGeneratorWorker popBuilder(@NotNull JetElement element) { 048 JetControlFlowInstructionsGeneratorWorker worker = builders.pop(); 049 if (!builders.isEmpty()) { 050 builder = builders.peek(); 051 } 052 else { 053 builder = null; 054 } 055 return worker; 056 } 057 058 @Override 059 public void enterSubroutine(@NotNull JetElement subroutine) { 060 if (builder != null && subroutine instanceof JetFunctionLiteral) { 061 pushBuilder(subroutine, builder.getReturnSubroutine()); 062 } 063 else { 064 pushBuilder(subroutine, subroutine); 065 } 066 assert builder != null; 067 builder.enterSubroutine(subroutine); 068 } 069 070 @Override 071 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) { 072 super.exitSubroutine(subroutine); 073 JetControlFlowInstructionsGeneratorWorker worker = popBuilder(subroutine); 074 if (!builders.empty()) { 075 JetControlFlowInstructionsGeneratorWorker builder = builders.peek(); 076 LocalDeclarationInstruction instruction = new LocalDeclarationInstruction(subroutine, worker.getPseudocode()); 077 builder.add(instruction); 078 } 079 return worker.getPseudocode(); 080 } 081 082 private class JetControlFlowInstructionsGeneratorWorker implements JetControlFlowBuilder { 083 084 private final PseudocodeImpl pseudocode; 085 private final Label error; 086 private final Label sink; 087 private final JetElement returnSubroutine; 088 089 private JetControlFlowInstructionsGeneratorWorker(@NotNull JetElement scopingElement, @NotNull JetElement returnSubroutine) { 090 this.pseudocode = new PseudocodeImpl(scopingElement); 091 this.error = pseudocode.createLabel("error"); 092 this.sink = pseudocode.createLabel("sink"); 093 this.returnSubroutine = returnSubroutine; 094 } 095 096 public PseudocodeImpl getPseudocode() { 097 return pseudocode; 098 } 099 100 private void add(@NotNull Instruction instruction) { 101 pseudocode.addInstruction(instruction); 102 } 103 104 @NotNull 105 @Override 106 public final Label createUnboundLabel() { 107 return pseudocode.createLabel("L" + labelCount++); 108 } 109 110 @NotNull 111 @Override 112 public Label createUnboundLabel(@NotNull String name) { 113 return pseudocode.createLabel("L" + labelCount++ + " [" + name + "]"); 114 } 115 116 @Override 117 public final LoopInfo enterLoop(@NotNull JetExpression expression, @Nullable Label loopExitPoint, Label conditionEntryPoint) { 118 Label loopEntryLabel = createUnboundLabel("loop entry point"); 119 bindLabel(loopEntryLabel); 120 LoopInfo blockInfo = new LoopInfo( 121 expression, 122 loopEntryLabel, 123 loopExitPoint != null ? loopExitPoint : createUnboundLabel("loop exit point"), 124 createUnboundLabel("body entry point"), 125 conditionEntryPoint != null ? conditionEntryPoint : createUnboundLabel("condition entry point")); 126 loopInfo.push(blockInfo); 127 elementToBlockInfo.put(expression, blockInfo); 128 allBlocks.push(blockInfo); 129 pseudocode.recordLoopInfo(expression, blockInfo); 130 return blockInfo; 131 } 132 133 @Override 134 public final void exitLoop(@NotNull JetExpression expression) { 135 BreakableBlockInfo info = loopInfo.pop(); 136 elementToBlockInfo.remove(expression); 137 allBlocks.pop(); 138 bindLabel(info.getExitPoint()); 139 } 140 141 @Override 142 public JetElement getCurrentLoop() { 143 return loopInfo.empty() ? null : loopInfo.peek().getElement(); 144 } 145 146 @Override 147 public void enterSubroutine(@NotNull JetElement subroutine) { 148 Label entryPoint = createUnboundLabel(); 149 BreakableBlockInfo blockInfo = new BreakableBlockInfo(subroutine, entryPoint, createUnboundLabel()); 150// subroutineInfo.push(blockInfo); 151 elementToBlockInfo.put(subroutine, blockInfo); 152 allBlocks.push(blockInfo); 153 bindLabel(entryPoint); 154 add(new SubroutineEnterInstruction(subroutine)); 155 } 156 157 @NotNull 158 @Override 159 public JetElement getCurrentSubroutine() { 160 return pseudocode.getCorrespondingElement(); 161 } 162 163 @Override 164 public JetElement getReturnSubroutine() { 165 return returnSubroutine;// subroutineInfo.empty() ? null : subroutineInfo.peek().getElement(); 166 } 167 168 @Override 169 public Label getEntryPoint(@NotNull JetElement labelElement) { 170 return elementToBlockInfo.get(labelElement).getEntryPoint(); 171 } 172 173 @Override 174 public Label getExitPoint(@NotNull JetElement labelElement) { 175 BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement); 176 assert blockInfo != null : labelElement.getText(); 177 return blockInfo.getExitPoint(); 178 } 179 180//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 181 182 private void handleJumpInsideTryFinally(Label jumpTarget) { 183 List<TryFinallyBlockInfo> finallyBlocks = new ArrayList<TryFinallyBlockInfo>(); 184 185 for (int i = allBlocks.size() - 1; i >= 0; i--) { 186 BlockInfo blockInfo = allBlocks.get(i); 187 if (blockInfo instanceof BreakableBlockInfo) { 188 BreakableBlockInfo breakableBlockInfo = (BreakableBlockInfo) blockInfo; 189 if (jumpTarget == breakableBlockInfo.getExitPoint() || jumpTarget == breakableBlockInfo.getEntryPoint() 190 || jumpTarget == error) { 191 for (int j = finallyBlocks.size() - 1; j >= 0; j--) { 192 finallyBlocks.get(j).generateFinallyBlock(); 193 } 194 break; 195 } 196 } 197 else if (blockInfo instanceof TryFinallyBlockInfo) { 198 TryFinallyBlockInfo tryFinallyBlockInfo = (TryFinallyBlockInfo) blockInfo; 199 finallyBlocks.add(tryFinallyBlockInfo); 200 } 201 } 202 } 203 204 @Override 205 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) { 206 bindLabel(getExitPoint(subroutine)); 207 pseudocode.addExitInstruction(new SubroutineExitInstruction(subroutine, "<END>")); 208 bindLabel(error); 209 pseudocode.addErrorInstruction(new SubroutineExitInstruction(subroutine, "<ERROR>")); 210 bindLabel(sink); 211 pseudocode.addSinkInstruction(new SubroutineSinkInstruction(subroutine, "<SINK>")); 212 elementToBlockInfo.remove(subroutine); 213 allBlocks.pop(); 214 return null; 215 } 216 217 @Override 218 public void returnValue(@NotNull JetExpression returnExpression, @NotNull JetElement subroutine) { 219 Label exitPoint = getExitPoint(subroutine); 220 handleJumpInsideTryFinally(exitPoint); 221 add(new ReturnValueInstruction(returnExpression, exitPoint)); 222 } 223 224 @Override 225 public void returnNoValue(@NotNull JetElement returnExpression, @NotNull JetElement subroutine) { 226 Label exitPoint = getExitPoint(subroutine); 227 handleJumpInsideTryFinally(exitPoint); 228 add(new ReturnNoValueInstruction(returnExpression, exitPoint)); 229 } 230 231 @Override 232 public void write(@NotNull JetElement assignment, @NotNull JetElement lValue) { 233 add(new WriteValueInstruction(assignment, lValue)); 234 } 235 236 @Override 237 public void declare(@NotNull JetParameter parameter) { 238 add(new VariableDeclarationInstruction(parameter)); 239 } 240 241 @Override 242 public void declare(@NotNull JetVariableDeclaration property) { 243 add(new VariableDeclarationInstruction(property)); 244 } 245 246 @Override 247 public void read(@NotNull JetElement element) { 248 add(new ReadValueInstruction(element)); 249 } 250 251 @Override 252 public void readUnit(@NotNull JetExpression expression) { 253 add(new ReadUnitValueInstruction(expression)); 254 } 255 256 @Override 257 public void jump(@NotNull Label label) { 258 handleJumpInsideTryFinally(label); 259 add(new UnconditionalJumpInstruction(label)); 260 } 261 262 @Override 263 public void jumpOnFalse(@NotNull Label label) { 264 handleJumpInsideTryFinally(label); 265 add(new ConditionalJumpInstruction(false, label)); 266 } 267 268 @Override 269 public void jumpOnTrue(@NotNull Label label) { 270 handleJumpInsideTryFinally(label); 271 add(new ConditionalJumpInstruction(true, label)); 272 } 273 274 @Override 275 public void bindLabel(@NotNull Label label) { 276 pseudocode.bindLabel(label); 277 } 278 279 @Override 280 public void nondeterministicJump(Label label) { 281 handleJumpInsideTryFinally(label); 282 add(new NondeterministicJumpInstruction(label)); 283 } 284 285 @Override 286 public void nondeterministicJump(List<Label> labels) { 287 //todo 288 //handleJumpInsideTryFinally(label); 289 add(new NondeterministicJumpInstruction(labels)); 290 } 291 292 @Override 293 public void jumpToError() { 294 handleJumpInsideTryFinally(error); 295 add(new UnconditionalJumpInstruction(error)); 296 } 297 298 @Override 299 public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) { 300 allBlocks.push(new TryFinallyBlockInfo(generationTrigger)); 301 } 302 303 @Override 304 public void throwException(@NotNull JetThrowExpression expression) { 305 handleJumpInsideTryFinally(error); 306 add(new ThrowExceptionInstruction(expression, error)); 307 } 308 309 public void exitTryFinally() { 310 BlockInfo pop = allBlocks.pop(); 311 assert pop instanceof TryFinallyBlockInfo; 312 } 313 314 @Override 315 public void unsupported(JetElement element) { 316 add(new UnsupportedElementInstruction(element)); 317 } 318 319 @Override 320 public void repeatPseudocode(@NotNull Label startLabel, @NotNull Label finishLabel) { 321 pseudocode.repeatPart(startLabel, finishLabel); 322 } 323 } 324 325 public static class TryFinallyBlockInfo extends BlockInfo { 326 private final GenerationTrigger finallyBlock; 327 328 private TryFinallyBlockInfo(GenerationTrigger finallyBlock) { 329 this.finallyBlock = finallyBlock; 330 } 331 332 public void generateFinallyBlock() { 333 finallyBlock.generate(); 334 } 335 } 336 337}