From bf6805099cee7af9c098af7de132aad826021280 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 13 May 2020 15:54:07 +0700 Subject: [PATCH] Exception stack trace analysis: support negative index/length reported from arraycopy GitOrigin-RevId: a1d20d0865cdeb5d7413fca5c379243b1ff343c8 --- .../DataflowExceptionAnalysisProvider.java | 5 ++ ...DataflowExceptionAnalysisProviderTest.java | 21 ++++++ ...rrayCopyIndexOutOfBoundsExceptionInfo.java | 74 +++++++++++++++++++ .../ArrayIndexOutOfBoundsExceptionInfo.java | 8 ++ 4 files changed, 108 insertions(+) create mode 100644 java/openapi/src/com/intellij/execution/filters/ArrayCopyIndexOutOfBoundsExceptionInfo.java diff --git a/java/java-impl/src/com/intellij/slicer/DataflowExceptionAnalysisProvider.java b/java/java-impl/src/com/intellij/slicer/DataflowExceptionAnalysisProvider.java index ea10af9bd34d..0ef2b1da35d6 100644 --- a/java/java-impl/src/com/intellij/slicer/DataflowExceptionAnalysisProvider.java +++ b/java/java-impl/src/com/intellij/slicer/DataflowExceptionAnalysisProvider.java @@ -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; } diff --git a/java/java-tests/testSrc/com/intellij/java/slicer/DataflowExceptionAnalysisProviderTest.java b/java/java-tests/testSrc/com/intellij/java/slicer/DataflowExceptionAnalysisProviderTest.java index d60de9418a20..290445c65078 100644 --- a/java/java-tests/testSrc/com/intellij/java/slicer/DataflowExceptionAnalysisProviderTest.java +++ b/java/java-tests/testSrc/com/intellij/java/slicer/DataflowExceptionAnalysisProviderTest.java @@ -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, diff --git a/java/openapi/src/com/intellij/execution/filters/ArrayCopyIndexOutOfBoundsExceptionInfo.java b/java/openapi/src/com/intellij/execution/filters/ArrayCopyIndexOutOfBoundsExceptionInfo.java new file mode 100644 index 000000000000..8622950fb697 --- /dev/null +++ b/java/openapi/src/com/intellij/execution/filters/ArrayCopyIndexOutOfBoundsExceptionInfo.java @@ -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); + } +} diff --git a/java/openapi/src/com/intellij/execution/filters/ArrayIndexOutOfBoundsExceptionInfo.java b/java/openapi/src/com/intellij/execution/filters/ArrayIndexOutOfBoundsExceptionInfo.java index 12cc24be9246..a1991274d0bd 100644 --- a/java/openapi/src/com/intellij/execution/filters/ArrayIndexOutOfBoundsExceptionInfo.java +++ b/java/openapi/src/com/intellij/execution/filters/ArrayIndexOutOfBoundsExceptionInfo.java @@ -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; }