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.google.common.collect.*;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.lang.cfg.Label;
023 import org.jetbrains.jet.lang.cfg.LoopInfo;
024 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.*;
025 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction;
026 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction;
027 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.jumps.NondeterministicJumpInstruction;
028 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction;
029 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineEnterInstruction;
030 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineExitInstruction;
031 import org.jetbrains.jet.lang.cfg.pseudocode.instructions.special.SubroutineSinkInstruction;
032 import org.jetbrains.jet.lang.cfg.pseudocodeTraverser.PseudocodeTraverserPackage;
033 import org.jetbrains.jet.lang.psi.JetElement;
034 import org.jetbrains.jet.lang.psi.JetExpression;
035
036 import java.util.*;
037
038 import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD;
039 import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;
040
041 public class PseudocodeImpl implements Pseudocode {
042
043 public class PseudocodeLabel implements Label {
044 private final String name;
045 private Integer targetInstructionIndex;
046
047
048 private PseudocodeLabel(String name) {
049 this.name = name;
050 }
051
052 @NotNull
053 @Override
054 public String getName() {
055 return name;
056 }
057
058 @Override
059 public String toString() {
060 return name;
061 }
062
063 public Integer getTargetInstructionIndex() {
064 return targetInstructionIndex;
065 }
066
067 public void setTargetInstructionIndex(int targetInstructionIndex) {
068 this.targetInstructionIndex = targetInstructionIndex;
069 }
070
071 @Nullable
072 private List<Instruction> resolve() {
073 assert targetInstructionIndex != null;
074 return mutableInstructionList.subList(getTargetInstructionIndex(), mutableInstructionList.size());
075 }
076
077 public Instruction resolveToInstruction() {
078 assert targetInstructionIndex != null;
079 return mutableInstructionList.get(targetInstructionIndex);
080 }
081
082 public Label copy() {
083 return new PseudocodeLabel("copy " + name);
084 }
085 }
086
087 private final List<Instruction> mutableInstructionList = new ArrayList<Instruction>();
088 private final List<Instruction> instructions = new ArrayList<Instruction>();
089
090 private final Map<JetElement, PseudoValue> elementsToValues = new HashMap<JetElement, PseudoValue>();
091
092 private Pseudocode parent = null;
093 private Set<LocalFunctionDeclarationInstruction> localDeclarations = null;
094 //todo getters
095 private final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
096 private final Map<JetExpression, LoopInfo> loopInfo = Maps.newHashMap();
097
098 private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>();
099
100 private final JetElement correspondingElement;
101 private SubroutineExitInstruction exitInstruction;
102 private SubroutineSinkInstruction sinkInstruction;
103 private SubroutineExitInstruction errorInstruction;
104 private boolean postPrecessed = false;
105
106 public PseudocodeImpl(JetElement correspondingElement) {
107 this.correspondingElement = correspondingElement;
108 }
109
110 @NotNull
111 @Override
112 public JetElement getCorrespondingElement() {
113 return correspondingElement;
114 }
115
116 @NotNull
117 @Override
118 public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() {
119 if (localDeclarations == null) {
120 localDeclarations = getLocalDeclarations(this);
121 }
122 return localDeclarations;
123 }
124
125 @NotNull
126 private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations(@NotNull Pseudocode pseudocode) {
127 Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet();
128 for (Instruction instruction : ((PseudocodeImpl)pseudocode).mutableInstructionList) {
129 if (instruction instanceof LocalFunctionDeclarationInstruction) {
130 localDeclarations.add((LocalFunctionDeclarationInstruction) instruction);
131 localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody()));
132 }
133 }
134 return localDeclarations;
135 }
136
137 @Override
138 @Nullable
139 public Pseudocode getParent() {
140 return parent;
141 }
142
143 private void setParent(Pseudocode parent) {
144 this.parent = parent;
145 }
146
147 @NotNull
148 public Pseudocode getRootPseudocode() {
149 Pseudocode parent = getParent();
150 while (parent != null) {
151 if (parent.getParent() == null) return parent;
152 parent = parent.getParent();
153 }
154 return this;
155 }
156
157 /*package*/ PseudocodeLabel createLabel(String name) {
158 PseudocodeLabel label = new PseudocodeLabel(name);
159 labels.add(label);
160 return label;
161 }
162
163 @Override
164 @NotNull
165 public List<Instruction> getInstructions() {
166 return instructions;
167 }
168
169 @NotNull
170 @Override
171 public List<Instruction> getReversedInstructions() {
172 LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet();
173 PseudocodeTraverserPackage.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null);
174 if (traversedInstructions.size() < instructions.size()) {
175 List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions);
176 Collections.reverse(simplyReversedInstructions);
177 for (Instruction instruction : simplyReversedInstructions) {
178 if (!traversedInstructions.contains(instruction)) {
179 PseudocodeTraverserPackage.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null);
180 }
181 }
182 }
183 return Lists.newArrayList(traversedInstructions);
184 }
185
186 @Override
187 @NotNull
188 public List<Instruction> getInstructionsIncludingDeadCode() {
189 return mutableInstructionList;
190 }
191
192 //for tests only
193 @NotNull
194 public List<PseudocodeLabel> getLabels() {
195 return labels;
196 }
197
198 /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) {
199 addInstruction(exitInstruction);
200 assert this.exitInstruction == null;
201 this.exitInstruction = exitInstruction;
202 }
203
204 /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) {
205 addInstruction(sinkInstruction);
206 assert this.sinkInstruction == null;
207 this.sinkInstruction = sinkInstruction;
208 }
209
210 /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) {
211 addInstruction(errorInstruction);
212 assert this.errorInstruction == null;
213 this.errorInstruction = errorInstruction;
214 }
215
216 /*package*/ void addInstruction(Instruction instruction) {
217 mutableInstructionList.add(instruction);
218 instruction.setOwner(this);
219
220 if (instruction instanceof JetElementInstruction) {
221 JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
222 representativeInstructions.put(elementInstruction.getElement(), instruction);
223 }
224 }
225
226 /*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) {
227 loopInfo.put(expression, blockInfo);
228 }
229
230 @Override
231 @NotNull
232 public SubroutineExitInstruction getExitInstruction() {
233 return exitInstruction;
234 }
235
236 @Override
237 @NotNull
238 public SubroutineSinkInstruction getSinkInstruction() {
239 return sinkInstruction;
240 }
241
242 @Override
243 @NotNull
244 public SubroutineEnterInstruction getEnterInstruction() {
245 return (SubroutineEnterInstruction) mutableInstructionList.get(0);
246 }
247
248 @Nullable
249 @Override
250 public PseudoValue getElementValue(@Nullable JetElement element) {
251 return elementsToValues.get(element);
252 }
253
254 /*package*/ void bindElementToValue(@NotNull JetElement element, @NotNull PseudoValue value) {
255 elementsToValues.put(element, value);
256 }
257
258 /*package*/ void bindLabel(Label label) {
259 ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size());
260 }
261
262 public void postProcess() {
263 if (postPrecessed) return;
264 postPrecessed = true;
265 errorInstruction.setSink(getSinkInstruction());
266 exitInstruction.setSink(getSinkInstruction());
267 int index = 0;
268 for (Instruction instruction : mutableInstructionList) {
269 //recursively invokes 'postProcess' for local declarations
270 processInstruction(instruction, index);
271 index++;
272 }
273 if (getParent() != null) return;
274
275 // Collecting reachable instructions should be done after processing all instructions
276 // (including instructions in local declarations) to avoid being in incomplete state.
277 collectAndCacheReachableInstructions();
278 for (LocalFunctionDeclarationInstruction localFunctionDeclarationInstruction : getLocalDeclarations()) {
279 ((PseudocodeImpl) localFunctionDeclarationInstruction.getBody()).collectAndCacheReachableInstructions();
280 }
281 }
282
283 private void collectAndCacheReachableInstructions() {
284 Set<Instruction> reachableInstructions = collectReachableInstructions();
285 for (Instruction instruction : mutableInstructionList) {
286 if (reachableInstructions.contains(instruction)) {
287 instructions.add(instruction);
288 }
289 }
290 markDeadInstructions();
291 }
292
293 private void processInstruction(Instruction instruction, final int currentPosition) {
294 instruction.accept(new InstructionVisitor() {
295 @Override
296 public void visitInstructionWithNext(InstructionWithNext instruction) {
297 instruction.setNext(getNextPosition(currentPosition));
298 }
299
300 @Override
301 public void visitJump(AbstractJumpInstruction instruction) {
302 instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
303 }
304
305 @Override
306 public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
307 instruction.setNext(getNextPosition(currentPosition));
308 List<Label> targetLabels = instruction.getTargetLabels();
309 for (Label targetLabel : targetLabels) {
310 instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel));
311 }
312 }
313
314 @Override
315 public void visitConditionalJump(ConditionalJumpInstruction instruction) {
316 Instruction nextInstruction = getNextPosition(currentPosition);
317 Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
318 if (instruction.getOnTrue()) {
319 instruction.setNextOnFalse(nextInstruction);
320 instruction.setNextOnTrue(jumpTarget);
321 }
322 else {
323 instruction.setNextOnFalse(jumpTarget);
324 instruction.setNextOnTrue(nextInstruction);
325 }
326 visitJump(instruction);
327 }
328
329 @Override
330 public void visitLocalFunctionDeclarationInstruction(LocalFunctionDeclarationInstruction instruction) {
331 PseudocodeImpl body = (PseudocodeImpl) instruction.getBody();
332 body.setParent(PseudocodeImpl.this);
333 body.postProcess();
334 instruction.setNext(getSinkInstruction());
335 }
336
337 @Override
338 public void visitSubroutineExit(SubroutineExitInstruction instruction) {
339 // Nothing
340 }
341
342 @Override
343 public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
344 // Nothing
345 }
346
347 @Override
348 public void visitInstruction(Instruction instruction) {
349 throw new UnsupportedOperationException(instruction.toString());
350 }
351 });
352 }
353
354 private Set<Instruction> collectReachableInstructions() {
355 Set<Instruction> visited = Sets.newHashSet();
356 PseudocodeTraverserPackage.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, null);
357 if (!visited.contains(getExitInstruction())) {
358 visited.add(getExitInstruction());
359 }
360 if (!visited.contains(errorInstruction)) {
361 visited.add(errorInstruction);
362 }
363 if (!visited.contains(getSinkInstruction())) {
364 visited.add(getSinkInstruction());
365 }
366 return visited;
367 }
368
369 private void markDeadInstructions() {
370 Set<Instruction> instructionSet = Sets.newHashSet(instructions);
371 for (Instruction instruction : mutableInstructionList) {
372 if (!instructionSet.contains(instruction)) {
373 ((InstructionImpl)instruction).setMarkedAsDead(true);
374 for (Instruction nextInstruction : instruction.getNextInstructions()) {
375 nextInstruction.getPreviousInstructions().remove(instruction);
376 }
377 }
378 }
379 }
380
381 @NotNull
382 private Instruction getJumpTarget(@NotNull Label targetLabel) {
383 return ((PseudocodeLabel)targetLabel).resolveToInstruction();
384 }
385
386 @NotNull
387 private Instruction getNextPosition(int currentPosition) {
388 int targetPosition = currentPosition + 1;
389 assert targetPosition < mutableInstructionList.size() : currentPosition;
390 return mutableInstructionList.get(targetPosition);
391 }
392
393 public void repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel) {
394 Integer startIndex = ((PseudocodeLabel) startLabel).getTargetInstructionIndex();
395 assert startIndex != null;
396 Integer finishIndex = ((PseudocodeLabel) finishLabel).getTargetInstructionIndex();
397 assert finishIndex != null;
398
399 Map<Label, Label> originalToCopy = Maps.newHashMap();
400 Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create();
401 for (PseudocodeLabel label : labels) {
402 Integer index = label.getTargetInstructionIndex();
403 if (index == null) continue; //label is not bounded yet
404 if (label == startLabel || label == finishLabel) continue;
405
406 if (startIndex <= index && index <= finishIndex) {
407 originalToCopy.put(label, label.copy());
408 originalLabelsForInstruction.put(getJumpTarget(label), label);
409 }
410 }
411 for (int index = startIndex; index < finishIndex; index++) {
412 Instruction originalInstruction = mutableInstructionList.get(index);
413 repeatLabelsBindingForInstruction(originalInstruction, originalToCopy, originalLabelsForInstruction);
414 addInstruction(copyInstruction(originalInstruction, originalToCopy));
415 }
416 repeatLabelsBindingForInstruction(mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction);
417 }
418
419 private void repeatLabelsBindingForInstruction(
420 @NotNull Instruction originalInstruction,
421 @NotNull Map<Label, Label> originalToCopy,
422 @NotNull Multimap<Instruction, Label> originalLabelsForInstruction
423 ) {
424 for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) {
425 bindLabel(originalToCopy.get(originalLabel));
426 }
427 }
428
429 private Instruction copyInstruction(@NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) {
430 if (instruction instanceof AbstractJumpInstruction) {
431 Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel();
432 if (originalToCopy.containsKey(originalTarget)) {
433 return ((AbstractJumpInstruction)instruction).copy(originalToCopy.get(originalTarget));
434 }
435 }
436 if (instruction instanceof NondeterministicJumpInstruction) {
437 List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels();
438 List<Label> copyTargets = copyLabels(originalTargets, originalToCopy);
439 return ((NondeterministicJumpInstruction) instruction).copy(copyTargets);
440 }
441 return ((InstructionImpl)instruction).copy();
442 }
443
444 @NotNull
445 private List<Label> copyLabels(Collection<Label> labels, Map<Label, Label> originalToCopy) {
446 List<Label> newLabels = Lists.newArrayList();
447 for (Label label : labels) {
448 Label newLabel = originalToCopy.get(label);
449 newLabels.add(newLabel != null ? newLabel : label);
450 }
451 return newLabels;
452 }
453 }