Exception stack trace analysis: support negative index/length reported from arraycopy

GitOrigin-RevId: a1d20d0865cdeb5d7413fca5c379243b1ff343c8
This commit is contained in:
Tagir Valeev
2020-05-13 10:29:26 +00:00
committed by intellij-monorepo-bot
parent 64a4cc2be6
commit bf6805099c
4 changed files with 108 additions and 0 deletions
@@ -129,6 +129,11 @@ public class DataflowExceptionAnalysisProvider implements ExceptionAnalysisProvi
else if (info instanceof ArithmeticExceptionInfo) {
return fromArithmeticException(anchor);
}
else if (info instanceof ArrayCopyIndexOutOfBoundsExceptionInfo) {
if (anchor instanceof PsiExpression) {
return Analysis.create(DfTypes.intValue(((ArrayCopyIndexOutOfBoundsExceptionInfo)info).getValue()), (PsiExpression)anchor);
}
}
return null;
}
@@ -288,6 +288,27 @@ public class DataflowExceptionAnalysisProviderTest extends LightJavaCodeInsightT
" void callee(String x, String y, String z) {}\n" +
"}");
}
public void testArrayCopySource() {
doTest("java.lang.ArrayIndexOutOfBoundsException: arraycopy: source index -1 out of bounds for int[10]\n" +
"\tat java.base/java.lang.System.arraycopy(Native Method)",
"Find why 'src' could be -1",
"class Test {static void test(int[] data, int src, int dst, int len) { System.arraycopy(data, src, data, dst, len); }}");
}
public void testArrayCopyDest() {
doTest("java.lang.ArrayIndexOutOfBoundsException: arraycopy: destination index -1 out of bounds for int[10]\n" +
"\tat java.base/java.lang.System.arraycopy(Native Method)",
"Find why 'dst' could be -1",
"class Test {static void test(int[] data, int src, int dst, int len) { System.arraycopy(data, src, data, dst, len); }}");
}
public void testArrayCopyLength() {
doTest("java.lang.ArrayIndexOutOfBoundsException: arraycopy: length -1 is negative\n" +
"\tat java.base/java.lang.System.arraycopy(Native Method)",
"Find why 'len' could be -1",
"class Test {static void test(int[] data, int src, int dst, int len) { System.arraycopy(data, src, data, dst, len); }}");
}
private void doTest(@NotNull String exceptionLine,
@Nullable("If no action is expected") String expectedActionTitle,
@@ -0,0 +1,74 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.execution.filters;
import com.intellij.psi.*;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ArrayCopyIndexOutOfBoundsExceptionInfo extends ExceptionInfo {
private static final String SOURCE_INDEX_PREFIX = "source index ";
private static final String DESTINATION_INDEX_PREFIX = "destination index ";
private static final String COMMON_PREFIX = "arraycopy: ";
private static final String LENGTH_PREFIX = "length ";
private final int myValue;
private final int myParameter;
public ArrayCopyIndexOutOfBoundsExceptionInfo(int offset, String message, int value, int parameter) {
super(offset, "java.lang.ArrayIndexOutOfBoundsException", message);
myValue = value;
myParameter = parameter;
}
public int getValue() {
return myValue;
}
@Override
@Nullable PsiElement matchSpecificExceptionElement(@NotNull PsiElement element) {
if (!(element instanceof PsiIdentifier)) return null;
if (!element.textMatches("arraycopy")) return null;
PsiElement ref = element.getParent();
if (!(ref instanceof PsiReferenceExpression)) return null;
PsiMethodCallExpression call = ObjectUtils.tryCast(ref.getParent(), PsiMethodCallExpression.class);
if (call == null) return null;
PsiExpression[] args = call.getArgumentList().getExpressions();
if (args.length != 5) return null;
PsiMethod method = call.resolveMethod();
if (method == null) return null;
PsiClass containingClass = method.getContainingClass();
if (containingClass == null || !"java.lang.System".equals(containingClass.getQualifiedName())) return null;
return args[myParameter];
}
public static ExceptionInfo tryCreate(int offset, String message) {
if (!message.startsWith(COMMON_PREFIX)) return null;
int parameter;
int indexOffset = COMMON_PREFIX.length();
if (message.startsWith(SOURCE_INDEX_PREFIX, COMMON_PREFIX.length())) {
parameter = 1;
indexOffset += SOURCE_INDEX_PREFIX.length();
}
else if (message.startsWith(DESTINATION_INDEX_PREFIX, COMMON_PREFIX.length())) {
parameter = 3;
indexOffset += DESTINATION_INDEX_PREFIX.length();
}
else if (message.startsWith(LENGTH_PREFIX, COMMON_PREFIX.length())) {
parameter = 4;
indexOffset += LENGTH_PREFIX.length();
} else {
return null;
}
int indexEndOffset = message.indexOf(' ', indexOffset);
if (indexEndOffset == -1) return null;
String indexStr = message.substring(indexOffset, indexEndOffset);
int index;
try {
index = Integer.parseInt(indexStr);
}
catch (NumberFormatException ignored) {
return null;
}
return new ArrayCopyIndexOutOfBoundsExceptionInfo(offset, message, index, parameter);
}
}
@@ -24,6 +24,14 @@ public class ArrayIndexOutOfBoundsExceptionInfo extends ExceptionInfo {
}
}
@Override
public ExceptionInfo consumeStackLine(String line) {
if (line.contains("java.lang.System.arraycopy")) {
return ArrayCopyIndexOutOfBoundsExceptionInfo.tryCreate(getClassNameOffset(), getExceptionMessage());
}
return super.consumeStackLine(line);
}
public @Nullable Integer getIndex() {
return myIndex;
}