Fix race conditions in ConstantExpressionVisitor.

When the same condition is concurrently evaluated in two threads, it was possible to get it not evaluated in one of threads:
1. Shared PsiElement user data could be cleared by one thread before used by another
2. Subsequent getCached() calls in visitElement and elementFinished may return null and non-null if value was cached in-between by another thread

This should fix flacky DataFlowRangeAnalysisTest.testLongRangeKnownMethods test as it reason was "Long.MIN_VALUE + 1" is not always calculated to constant expression resulting in different CFG for the same code
This commit is contained in:
Tagir Valeev
2017-04-24 15:25:38 +07:00
parent 05a83bb49e
commit 41d910fb2d
2 changed files with 15 additions and 16 deletions
@@ -15,15 +15,16 @@
*/
package com.intellij.psi.impl;
import com.intellij.openapi.util.Key;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.ConstantEvaluationOverflowException;
import com.intellij.psi.util.ConstantExpressionUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.StringInterner;
import gnu.trove.THashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
@@ -32,6 +33,7 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan
private final StringInterner myInterner = new StringInterner();
private Set<PsiVariable> myVisitedVars;
private Map<PsiElement, Object> myCachedValues = new HashMap<>();
private final boolean myThrowExceptionOnOverflow;
private Object myResult;
@@ -50,20 +52,13 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan
store(element, myResult);
return myResult;
}
private static final Key<Object> VALUE = Key.create("VALUE");
private static Object getStoredValue(PsiElement element) {
if (element == null) {
return null;
}
try {
return element.getUserData(VALUE);
}
finally {
element.putUserData(VALUE, null);
}
private Object getStoredValue(PsiElement element) {
return myCachedValues.remove(element);
}
static void store(PsiElement element, Object value) {
element.putUserData(VALUE, value);
void store(PsiElement element, Object value) {
myCachedValues.put(element, value);
}
@Override
@@ -543,6 +538,7 @@ class ConstantExpressionVisitor extends JavaElementVisitor implements PsiConstan
String name = ClassUtil.getJVMClassName((PsiClass)element);
try {
Class aClass = Class.forName(name);
//noinspection unchecked
myResult = Enum.valueOf(aClass, constant);
}
catch (Throwable ignore) { }
@@ -64,6 +64,9 @@ public class JavaConstantExpressionEvaluator extends JavaRecursiveElementWalking
Object result = myConstantExpressionVisitor.handle(element);
cache(element, result);
}
else {
myConstantExpressionVisitor.store(element, value == NO_VALUE ? null : value);
}
}
@Override
@@ -74,7 +77,7 @@ public class JavaConstantExpressionEvaluator extends JavaRecursiveElementWalking
// will cache back in elementFinished()
}
else {
ConstantExpressionVisitor.store(element, value == NO_VALUE ? null : value);
myConstantExpressionVisitor.store(element, value == NO_VALUE ? null : value);
}
}
@@ -122,7 +125,7 @@ public class JavaConstantExpressionEvaluator extends JavaRecursiveElementWalking
PsiElement operand = ((PsiPrefixExpression)expression).getOperand();
if (operand == null) return null;
Object value = evaluator.myConstantExpressionVisitor.handle(operand);
ConstantExpressionVisitor.store(operand, value);
evaluator.myConstantExpressionVisitor.store(operand, value);
}
return evaluator.myConstantExpressionVisitor.handle(expression);
}