From f257da5961a30b45fcfcfeac6b8c8cbf196ab544 Mon Sep 17 00:00:00 2001 From: Ilya Klyuchnikov Date: Wed, 12 Nov 2014 16:24:52 +0300 Subject: [PATCH] sharing data per class analysis instead of per thread --- .../bytecodeAnalysis/Analysis.java | 7 ---- .../bytecodeAnalysis/ClassDataIndexer.java | 18 +++++++--- .../bytecodeAnalysis/Contracts.java | 5 +-- .../bytecodeAnalysis/Parameters.java | 36 ++++++++----------- 4 files changed, 30 insertions(+), 36 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java index 61d018f2f5ae..5919ad6e6bb4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Analysis.java @@ -203,13 +203,6 @@ abstract class Analysis { public static final int STEPS_LIMIT = 30000; public static final int EQUATION_SIZE_LIMIT = 30; - protected static final ThreadLocal ourPendingStates = new ThreadLocal() { - @Override - protected State[] initialValue() { - return new State[STEPS_LIMIT]; - } - }; - final RichControlFlow richControlFlow; final Direction direction; final ControlFlowGraph controlFlow; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java index 900c137c7c1f..97d7b390987a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java @@ -77,6 +77,13 @@ public class ClassDataIndexer implements DataIndexer>> processClass(final ClassReader classReader, final String presentableUrl) { + // It is OK to share pending states, actions and results for analyses. + // Analyses are designed in such a way that they first write to states/actions/results and then read only those portion + // of states/actions/results which were written by the current pass of the analysis. + // Since states/actions/results are quite expensive to create (32K array) for each analysis, we create them once per class analysis. + final State[] sharedPendingStates = new State[Analysis.STEPS_LIMIT]; + final PendingAction[] sharedPendingActions = new PendingAction[Analysis.STEPS_LIMIT]; + final PResults.PResult[] sharedResults = new PResults.PResult[Analysis.STEPS_LIMIT]; final Map>> equations = new HashMap>>(); classReader.accept(new ClassVisitor(Opcodes.ASM5) { @@ -215,7 +222,7 @@ public class ClassDataIndexer implements DataIndexer outEquation = isInterestingResult ? - new InOutAnalysis(richControlFlow, Out, origins, stable).analyze() : + new InOutAnalysis(richControlFlow, Out, origins, stable, sharedPendingStates).analyze() : null; if (isReferenceResult) { @@ -229,7 +236,8 @@ public class ClassDataIndexer implements DataIndexer notNullParamEquation = notNullInAnalysis.analyze(); possibleNPE = notNullInAnalysis.possibleNPE; notNullParam = notNullParamEquation.rhs.equals(FINAL_NOT_NULL); @@ -245,7 +253,7 @@ public class ClassDataIndexer implements DataIndexer(new Key(method, new In(i, In.NULLABLE_MASK), stable), FINAL_TOP)); } else { - result.add(new NullableInAnalysis(richControlFlow, new In(i, In.NULLABLE_MASK), stable).analyze()); + result.add(new NullableInAnalysis(richControlFlow, new In(i, In.NULLABLE_MASK), stable, sharedPendingStates).analyze()); } } else { @@ -260,9 +268,9 @@ public class ClassDataIndexer implements DataIndexer..." analysis - result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable).analyze()); + result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.Null), origins, stable, sharedPendingStates).analyze()); } - result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable).analyze()); + result.add(new InOutAnalysis(richControlFlow, new InOut(i, Value.NotNull), origins, stable, sharedPendingStates).analyze()); } else { // parameter is not leaking, so a contract is the same as for the whole method diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java index 46ec090db16c..2134b0b585e9 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Contracts.java @@ -41,7 +41,7 @@ class InOutAnalysis extends Analysis> { static final ResultUtil resultUtil = new ResultUtil(new ELattice(Value.Bot, Value.Top)); - final private State[] pending = ourPendingStates.get(); + final private State[] pending; private final InOutInterpreter interpreter; private final Value inValue; private final int generalizeShift; @@ -49,8 +49,9 @@ class InOutAnalysis extends Analysis> { private int id = 0; private int pendingTop = 0; - protected InOutAnalysis(RichControlFlow richControlFlow, Direction direction, boolean[] resultOrigins, boolean stable) { + protected InOutAnalysis(RichControlFlow richControlFlow, Direction direction, boolean[] resultOrigins, boolean stable, State[] pending) { super(richControlFlow, direction, stable); + this.pending = pending; interpreter = new InOutInterpreter(direction, richControlFlow.controlFlow.methodNode.instructions, resultOrigins); inValue = direction instanceof InOut ? ((InOut)direction).inValue : null; generalizeShift = (methodNode.access & ACC_STATIC) == 0 ? 1 : 0; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java index 8bf3f5961a89..28188fc42786 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Parameters.java @@ -167,30 +167,21 @@ class MakeResult implements PendingAction { } class NonNullInAnalysis extends Analysis { - private static final ThreadLocal ourPending = new ThreadLocal() { - @Override - protected PendingAction[] initialValue() { - return new PendingAction[Analysis.STEPS_LIMIT]; - } - }; - private static final ThreadLocal ourResults = new ThreadLocal() { - @Override - protected PResult[] initialValue() { - return new PResult[Analysis.STEPS_LIMIT]; - } - }; - - final private PendingAction[] pending = ourPending.get(); - + final private PendingAction[] pendingActions; + private final PResult[] results; private final NotNullInterpreter interpreter = new NotNullInterpreter(); - private PResult[] results; // Flag saying that at some branch NPE was found. Used later as an evidence that this param is *NOT* @Nullable (optimization). boolean possibleNPE; - protected NonNullInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable) { + protected NonNullInAnalysis(RichControlFlow richControlFlow, + Direction direction, + boolean stable, + PendingAction[] pendingActions, + PResult[] results) { super(richControlFlow, direction, stable); - results = ourResults.get(); + this.pendingActions = pendingActions; + this.results = results; } PResult combineResults(PResult delta, int[] subResults) throws AnalyzerException { @@ -232,7 +223,7 @@ class NonNullInAnalysis extends Analysis { if (steps >= STEPS_LIMIT) { throw new AnalyzerException(null, "limit is reached, steps: " + steps + " in method " + method); } - PendingAction action = pending[--pendingTop]; + PendingAction action = pendingActions[--pendingTop]; if (action instanceof MakeResult) { MakeResult makeResult = (MakeResult) action; PResult result = combineResults(makeResult.subResult, makeResult.indices); @@ -397,7 +388,7 @@ class NonNullInAnalysis extends Analysis { if (pendingTop >= STEPS_LIMIT) { throw new AnalyzerException(null, "limit is reached in method " + method); } - pending[pendingTop++] = action; + pendingActions[pendingTop++] = action; } private void execute(Frame frame, AbstractInsnNode insnNode) throws AnalyzerException { @@ -418,12 +409,13 @@ class NonNullInAnalysis extends Analysis { } class NullableInAnalysis extends Analysis { - final private State[] pending = ourPendingStates.get(); + final private State[] pending; private final NullableInterpreter interpreter = new NullableInterpreter(); - protected NullableInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable) { + protected NullableInAnalysis(RichControlFlow richControlFlow, Direction direction, boolean stable, State[] pending) { super(richControlFlow, direction, stable); + this.pending = pending; } @NotNull