| /* |
| * 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; |
| } |
| } |
| |