blob: 3159193bce4f94324fe18e05431e33513a9bbacd [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
/*
* Created by IntelliJ IDEA.
* User: cdr
* Date: Aug 13, 2002
* Time: 12:07:14 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij.psi.controlFlow;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NotNullLazyKey;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.util.containers.ConcurrentList;
import com.intellij.util.containers.ConcurrentWeakHashMap;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentMap;
public class ControlFlowFactory {
// psiElements hold weakly, controlFlows softly
private final ConcurrentMap<PsiElement, Reference<ConcurrentList<ControlFlowContext>>> cachedFlows = new ConcurrentWeakHashMap<PsiElement, Reference<ConcurrentList<ControlFlowContext>>>();
private static final NotNullLazyKey<ControlFlowFactory, Project> INSTANCE_KEY = ServiceManager.createLazyKey(ControlFlowFactory.class);
public static ControlFlowFactory getInstance(Project project) {
return INSTANCE_KEY.getValue(project);
}
public ControlFlowFactory(PsiManagerEx psiManager) {
psiManager.registerRunnableToRunOnChange(new Runnable(){
@Override
public void run() {
clearCache();
}
});
}
private void clearCache() {
cachedFlows.clear();
}
@Deprecated
public void registerSubRange(final PsiElement codeFragment, final ControlFlowSubRange flow, final boolean evaluateConstantIfConfition,
final ControlFlowPolicy policy) {
registerSubRange(codeFragment, flow, evaluateConstantIfConfition, true, policy);
}
public void registerSubRange(final PsiElement codeFragment,
final ControlFlowSubRange flow,
final boolean evaluateConstantIfConfition,
boolean enableShortCircuit, final ControlFlowPolicy policy) {
registerControlFlow(codeFragment, flow, evaluateConstantIfConfition, enableShortCircuit, policy);
}
private static class ControlFlowContext {
private final ControlFlowPolicy policy;
private final boolean evaluateConstantIfCondition;
private final boolean enableShortCircuit;
private final long modificationCount;
private final ControlFlow controlFlow;
private ControlFlowContext(boolean evaluateConstantIfCondition, boolean enableShortCircuit, @NotNull ControlFlowPolicy policy, long modificationCount, @NotNull ControlFlow controlFlow) {
this.evaluateConstantIfCondition = evaluateConstantIfCondition;
this.enableShortCircuit = enableShortCircuit;
this.policy = policy;
this.modificationCount = modificationCount;
this.controlFlow = controlFlow;
}
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ControlFlowContext that = (ControlFlowContext)o;
return isFor(that);
}
public int hashCode() {
int result = policy.hashCode();
result = 31 * result + (evaluateConstantIfCondition ? 1 : 0);
result = 31 * result + (int)(modificationCount ^ (modificationCount >>> 32));
return result;
}
public boolean isFor(@NotNull ControlFlowPolicy policy, final boolean evaluateConstantIfCondition, final boolean enableShortCircuit, long modificationCount) {
if (modificationCount != this.modificationCount) return false;
if (!policy.equals(this.policy)) return false;
if (enableShortCircuit != this.enableShortCircuit) return false;
// optimization: when no constant condition were computed, both control flows are the same
if (!controlFlow.isConstantConditionOccurred()) return true;
return evaluateConstantIfCondition == this.evaluateConstantIfCondition;
}
private boolean isFor(@NotNull ControlFlowContext that) {
return isFor(that.policy, that.evaluateConstantIfCondition, that.enableShortCircuit, that.modificationCount);
}
}
@NotNull
public ControlFlow getControlFlow(@NotNull PsiElement element, @NotNull ControlFlowPolicy policy) throws AnalysisCanceledException {
return getControlFlow(element, policy, true, true);
}
@NotNull
public ControlFlow getControlFlow(@NotNull PsiElement element, @NotNull ControlFlowPolicy policy, boolean evaluateConstantIfCondition) throws AnalysisCanceledException {
return getControlFlow(element, policy, true, evaluateConstantIfCondition);
}
@NotNull
public ControlFlow getControlFlow(@NotNull PsiElement element,
@NotNull ControlFlowPolicy policy,
boolean enableShortCircuit,
boolean evaluateConstantIfCondition) throws AnalysisCanceledException {
final long modificationCount = element.getManager().getModificationTracker().getModificationCount();
ConcurrentList<ControlFlowContext> cached = getOrCreateCachedFlowsForElement(element);
for (ControlFlowContext context : cached) {
if (context.isFor(policy, evaluateConstantIfCondition, enableShortCircuit, modificationCount)) return context.controlFlow;
}
ControlFlow controlFlow = new ControlFlowAnalyzer(element, policy, enableShortCircuit, evaluateConstantIfCondition).buildControlFlow();
ControlFlowContext context = createContext(evaluateConstantIfCondition, enableShortCircuit, policy, controlFlow, modificationCount);
cached.addIfAbsent(context);
return controlFlow;
}
@NotNull
private static ControlFlowContext createContext(final boolean evaluateConstantIfCondition,
boolean enableShortCircuit,
@NotNull ControlFlowPolicy policy,
@NotNull ControlFlow controlFlow,
final long modificationCount) {
return new ControlFlowContext(evaluateConstantIfCondition, enableShortCircuit, policy, modificationCount,controlFlow);
}
private void registerControlFlow(@NotNull PsiElement element,
@NotNull ControlFlow flow,
boolean evaluateConstantIfCondition,
boolean enableShortCircuit,
@NotNull ControlFlowPolicy policy) {
final long modificationCount = element.getManager().getModificationTracker().getModificationCount();
ControlFlowContext controlFlowContext = createContext(evaluateConstantIfCondition, enableShortCircuit, policy, flow, modificationCount);
ConcurrentList<ControlFlowContext> cached = getOrCreateCachedFlowsForElement(element);
cached.addIfAbsent(controlFlowContext);
}
@NotNull
private ConcurrentList<ControlFlowContext> getOrCreateCachedFlowsForElement(@NotNull PsiElement element) {
Reference<ConcurrentList<ControlFlowContext>> cachedRef = cachedFlows.get(element);
ConcurrentList<ControlFlowContext> cached = com.intellij.reference.SoftReference.dereference(cachedRef);
if (cached == null) {
cached = ContainerUtil.createConcurrentList();
cachedFlows.put(element, new SoftReference<ConcurrentList<ControlFlowContext>>(cached));
}
return cached;
}
}