Java control flow: Temporarily revert detecting redundant assignment when the control flow includes a try-finally statement, because it consumes too much memory when handling nested finally blocks (IDEA-155836)

This commit is contained in:
Pavel Dolgov
2016-08-09 13:58:17 +03:00
parent aea4bfbcb7
commit fad7cb2239
16 changed files with 57 additions and 440 deletions
@@ -23,13 +23,13 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.containers.IntArrayList;
import com.intellij.util.containers.Queue;
import com.intellij.util.containers.Stack;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author max
@@ -64,71 +64,67 @@ public class DefUseUtil {
}
}
private static class InstructionState implements Comparable<InstructionState> {
private Set<PsiVariable> myUsed;
private final InstructionKey myInstructionKey;
private final List<InstructionKey> myBackwardTraces;
private static class InstructionState {
private Set<PsiVariable> myVariablesUseArmed;
private final int myInstructionIdx;
private final IntArrayList myBackwardTraces;
private boolean myIsVisited;
public InstructionState(@NotNull InstructionKey instructionKey) {
myInstructionKey = instructionKey;
myBackwardTraces = new ArrayList<InstructionKey>();
myUsed = null;
public InstructionState(int instructionIdx) {
myInstructionIdx = instructionIdx;
myBackwardTraces = new IntArrayList();
myVariablesUseArmed = null;
}
public void addBackwardTrace(InstructionKey key) {
myBackwardTraces.add(key);
public void addBackwardTrace(int i) {
myBackwardTraces.add(i);
}
public List<InstructionKey> getBackwardTraces() {
public IntArrayList getBackwardTraces() {
return myBackwardTraces;
}
public InstructionKey getInstructionKey() {
return myInstructionKey;
public int getInstructionIdx() {
return myInstructionIdx;
}
void addUsed(PsiVariable psiVariable) {
void mergeUseArmed(PsiVariable psiVariable) {
touch();
myUsed.add(psiVariable);
myVariablesUseArmed.add(psiVariable);
}
boolean removeUsed(PsiVariable psiVariable) {
boolean mergeUseDisarmed(PsiVariable psiVariable) {
touch();
return myUsed.remove(psiVariable);
boolean result = myVariablesUseArmed.contains(psiVariable);
myVariablesUseArmed.remove(psiVariable);
return result;
}
private void touch() {
if (myUsed == null) myUsed = new THashSet<PsiVariable>();
if (myVariablesUseArmed == null) myVariablesUseArmed = new THashSet<PsiVariable>();
}
public void addUsedFrom(InstructionState state) {
public void merge(InstructionState state) {
touch();
myUsed.addAll(state.myUsed);
myVariablesUseArmed.addAll(state.myVariablesUseArmed);
}
public boolean contains(InstructionState state) {
return myUsed != null && state.myUsed != null &&
myUsed.containsAll(state.myUsed);
return myVariablesUseArmed != null && state.myVariablesUseArmed != null &&
myVariablesUseArmed.containsAll(state.myVariablesUseArmed);
}
public void markVisited() {
public boolean markVisited() {
boolean old = myIsVisited;
myIsVisited = true;
return old;
}
public boolean isVisited() {
return myIsVisited;
}
@Override
public int compareTo(@NotNull InstructionState other) {
return myInstructionKey.compareTo(other.myInstructionKey);
}
@Override
public String toString() {
return myInstructionKey + " " + myBackwardTraces + (myIsVisited ? "(v)" : "(n)") + " " + (myUsed != null ? myUsed : "-");
}
}
@Nullable
@@ -169,11 +165,9 @@ public class DefUseUtil {
}
}
Map<InstructionKey, InstructionState> stateMap = getStates(instructions);
InstructionState[] states = stateMap.values().toArray(new InstructionState[0]);
Arrays.sort(states);
InstructionState[] states = getStates(instructions);
BitSet usefulWrites = new BitSet(instructions.size());
boolean[] defsArmed = new boolean[instructions.size()];
Queue<InstructionState> queue = new Queue<InstructionState>(8);
@@ -184,7 +178,7 @@ public class DefUseUtil {
for (PsiVariable psiVariable : assignedVariables) {
if (psiVariable instanceof PsiField) {
outerState.addUsed(psiVariable);
outerState.mergeUseArmed(psiVariable);
}
}
queue.addLast(outerState);
@@ -194,21 +188,21 @@ public class DefUseUtil {
InstructionState state = queue.pullFirst();
state.markVisited();
InstructionKey key = state.getInstructionKey();
if (key.getOffset() < instructions.size()) {
Instruction instruction = instructions.get(key.getOffset());
int idx = state.getInstructionIdx();
if (idx < instructions.size()) {
Instruction instruction = instructions.get(idx);
if (instruction instanceof WriteVariableInstruction) {
WriteVariableInstruction writeInstruction = (WriteVariableInstruction)instruction;
PsiVariable psiVariable = writeInstruction.variable;
outUsedVariables.add(psiVariable);
if (state.removeUsed(psiVariable)) {
usefulWrites.set(key.getOffset());
if (state.mergeUseDisarmed(psiVariable)) {
defsArmed[idx] = true;
}
}
else if (instruction instanceof ReadVariableInstruction) {
ReadVariableInstruction readInstruction = (ReadVariableInstruction)instruction;
state.addUsed(readInstruction.variable);
state.mergeUseArmed(readInstruction.variable);
outUsedVariables.add(readInstruction.variable);
}
else {
@@ -216,11 +210,12 @@ public class DefUseUtil {
}
}
List<InstructionKey> backwardTraces = state.getBackwardTraces();
for (InstructionKey prevKeys : backwardTraces) {
InstructionState prevState = stateMap.get(prevKeys);
if (prevState != null && !prevState.contains(state)) {
prevState.addUsedFrom(state);
IntArrayList backwardTraces = state.getBackwardTraces();
for (int j = 0; j < backwardTraces.size(); j++) {
int prevIdx = backwardTraces.get(j);
InstructionState prevState = states[prevIdx];
if (!prevState.contains(state)) {
prevState.merge(state);
queue.addLast(prevState);
}
}
@@ -233,7 +228,7 @@ public class DefUseUtil {
Instruction instruction = instructions.get(i);
if (instruction instanceof WriteVariableInstruction) {
WriteVariableInstruction writeInstruction = (WriteVariableInstruction)instruction;
if (!usefulWrites.get(i)) {
if (!defsArmed[i]) {
PsiElement context = PsiTreeUtil.getNonStrictParentOfType(flow.getElement(i),
PsiStatement.class, PsiAssignmentExpression.class,
PsiPostfixExpression.class, PsiPrefixExpression.class);
@@ -259,16 +254,16 @@ public class DefUseUtil {
public static PsiElement[] getDefs(PsiCodeBlock body, final PsiVariable def, PsiElement ref) {
try {
RefsDefs refsDefs = new RefsDefs(body) {
private final IntArrayList[] myBackwardTraces = getBackwardTraces(instructions);
private final InstructionState[] states = getStates(instructions);
@Override
protected int nNext(int index) {
return myBackwardTraces[index].size();
return states[index].getBackwardTraces().size();
}
@Override
protected int getNext(int index, int no) {
return myBackwardTraces[index].get(no);
return states[index].getBackwardTraces().get(no);
}
@Override
@@ -412,7 +407,7 @@ public class DefUseUtil {
}
}
// hack: ControlFlow doesn't contains parameters initialization
// hack: ControlFlow doesnn't contains parameters initialization
if (index == 0 && def instanceof PsiParameter) {
res.add(def.getNameIdentifier());
}
@@ -447,10 +442,10 @@ public class DefUseUtil {
}
private static IntArrayList[] getBackwardTraces(final List<Instruction> instructions) {
final IntArrayList[] states = new IntArrayList[instructions.size()];
private static InstructionState[] getStates(final List<Instruction> instructions) {
final InstructionState[] states = new InstructionState[instructions.size()];
for (int i = 0; i < states.length; i++) {
states[i] = new IntArrayList();
states[i] = new InstructionState(i);
}
for (int i = 0; i < instructions.size(); i++) {
@@ -458,105 +453,13 @@ public class DefUseUtil {
for (int j = 0; j != instruction.nNext(); ++ j) {
final int next = instruction.getNext(i, j);
if (next < states.length) {
states[next].add(i);
states[next].addBackwardTrace(i);
}
}
}
return states;
}
private static Map<InstructionKey, InstructionState> getStates(final List<Instruction> instructions) {
class WalkThroughStack {
private final com.intellij.util.containers.Stack<InstructionKey> myFrom;
private final com.intellij.util.containers.Stack<InstructionKey> myNext;
WalkThroughStack(int size) {
if (size < 2) size = 2;
myFrom = new Stack<InstructionKey>(size);
myNext = new Stack<InstructionKey>(size);
}
void push(InstructionKey fromKey, InstructionKey nextKey) {
myFrom.push(fromKey);
myNext.push(nextKey);
}
InstructionKey peekFrom() {
return myFrom.peek();
}
InstructionKey popNext() {
myFrom.pop();
return myNext.pop();
}
boolean isEmpty() {
return myFrom.isEmpty();
}
}
class Walker {
private final Map<InstructionKey, InstructionState> myStates;
private final WalkThroughStack myWalkThroughStack;
Walker() {
myStates = new THashMap<InstructionKey, InstructionState>(instructions.size());
myWalkThroughStack = new WalkThroughStack(instructions.size() / 2);
}
Map<InstructionKey, InstructionState> walk() {
InstructionKey startKey = InstructionKey.create(0);
myStates.put(startKey, new InstructionState(startKey));
myWalkThroughStack.push(InstructionKey.create(-1), startKey);
Set<InstructionKey> visited = new THashSet<InstructionKey>(instructions.size());
while (!myWalkThroughStack.isEmpty()) {
InstructionKey fromKey = myWalkThroughStack.peekFrom();
InstructionKey nextKey = myWalkThroughStack.popNext();
addBackwardTrace(fromKey, nextKey);
if (!visited.contains(nextKey)) {
visit(nextKey);
visited.add(nextKey);
}
}
return myStates;
}
private void visit(InstructionKey fromKey) {
if (fromKey.getOffset() >= instructions.size()) return;
final Instruction instruction = instructions.get(fromKey.getOffset());
if (instruction instanceof CallInstruction) {
int nextOffset = ((CallInstruction)instruction).procBegin;
int returnOffset = fromKey.getOffset() + 1;
InstructionKey nextKey = fromKey.push(nextOffset, returnOffset);
myWalkThroughStack.push(fromKey, nextKey);
}
else if (instruction instanceof ReturnInstruction) {
int overriddenOffset = ((ReturnInstruction)instruction).offset;
InstructionKey nextKey = fromKey.pop(overriddenOffset);
myWalkThroughStack.push(fromKey, nextKey);
}
else {
for (int no = 0; no != instruction.nNext(); no++) {
final int nextOffset = instruction.getNext(fromKey.getOffset(), no);
InstructionKey nextKey = fromKey.next(nextOffset);
myWalkThroughStack.push(fromKey, nextKey);
}
}
}
private void addBackwardTrace(InstructionKey fromKey, InstructionKey nextKey) {
if (fromKey.getOffset() >= 0 && nextKey.getOffset() < instructions.size()) {
InstructionState state = myStates.get(nextKey);
if (state == null) myStates.put(nextKey, state = new InstructionState(nextKey));
state.addBackwardTrace(fromKey);
}
}
}
return new Walker().walk();
}
private static final ControlFlowPolicy ourPolicy = new ControlFlowPolicy() {
@Override
public PsiVariable getUsedVariable(@NotNull PsiReferenceExpression refExpr) {
@@ -1,108 +0,0 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.controlFlow;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
/**
* @author Pavel.Dolgov
*/
class InstructionKey implements Comparable<InstructionKey> {
private final int myOffset;
private final int[] myCallStack;
private InstructionKey(int offset, @NotNull int[] callStack) {
myOffset = offset;
myCallStack = callStack;
}
@NotNull
static InstructionKey create(int offset) {
return new InstructionKey(offset, ArrayUtil.EMPTY_INT_ARRAY);
}
InstructionKey next(int nextOffset) {
return new InstructionKey(nextOffset, myCallStack);
}
InstructionKey push(int nextOffset, int returnOffset) {
int[] nextStack = ArrayUtil.append(myCallStack, returnOffset);
return new InstructionKey(nextOffset, nextStack);
}
InstructionKey pop(int overriddenOffset) {
int returnOffset = myCallStack[myCallStack.length - 1];
int[] nextStack = ArrayUtil.realloc(myCallStack, myCallStack.length - 1);
int nextOffset = overriddenOffset != 0 ? overriddenOffset : returnOffset;
return new InstructionKey(nextOffset, nextStack);
}
int getOffset() {
return myOffset;
}
int[] getCallStack() {
return myCallStack;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InstructionKey key = (InstructionKey)o;
if (myOffset != key.myOffset) return false;
if (!Arrays.equals(myCallStack, key.myCallStack)) return false;
return true;
}
@Override
public int hashCode() {
int result = myOffset;
result = 31 * result + Arrays.hashCode(myCallStack);
return result;
}
@Override
public String toString() {
if (myCallStack.length == 0) {
return String.valueOf(myOffset);
}
StringBuilder s = new StringBuilder();
for (int offset : myCallStack) {
if (s.length() != 0) s.append(',');
s.append(offset);
}
return myOffset + "(" + s + ")";
}
@Override
public int compareTo(@NotNull InstructionKey key) {
int c = myOffset - key.myOffset;
if (c != 0) return c;
for (int i = 0, len = Math.min(myCallStack.length, key.myCallStack.length); i < len; i++) {
c = myCallStack[i] - key.myCallStack[i];
if (c != 0) return c;
}
c = myCallStack.length - key.myCallStack.length;
return c;
}
}