From f595ab86cf0485b9f855edbaf888232002232d2a Mon Sep 17 00:00:00 2001 From: sougandhs Date: Thu, 31 Oct 2024 21:55:27 +0530 Subject: [PATCH] Filtering unqualified Stacktraces #115 Using method signature to filter unqualified stacktraces generated in java stacktrace console. + Includes code review changes Fixes https://github.com/eclipse-jdt/eclipse.jdt.debug/issues/115 --- .../console/JavaStackTraceAmbiguityTest.java | 81 ++++++++++++++ .../AmbiguityTest/InnerClassTest.java | 4 +- .../AmbiguityTest/MyScheduledExecutor.java | 32 ++++++ .../AmbiguityTest/a/MyScheduledExecutor.java | 26 +++++ .../testfiles/AmbiguityTest/a/Sample.java | 4 +- .../AmbiguityTest/b/InnerClassTest.java | 32 ++++++ .../AmbiguityTest/c/InnerClassTest.java | 32 ++++++ .../ui/console/JavaStackTraceHyperlink.java | 100 +++++++++++++----- 8 files changed, 279 insertions(+), 32 deletions(-) create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/MyScheduledExecutor.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/MyScheduledExecutor.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/InnerClassTest.java create mode 100644 org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/InnerClassTest.java diff --git a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java index 38a44d8cd1..615cfcd80f 100644 --- a/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java +++ b/org.eclipse.jdt.debug.tests/console tests/org/eclipse/jdt/debug/tests/console/JavaStackTraceAmbiguityTest.java @@ -327,6 +327,87 @@ public void testLinkNavigationTrueForInnerClassMultilevel() throws Exception { } } + public void testLinkNavigationTrueForInnerClassParameters() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("MyScheduledExecutor(Worker).doWork(MyScheduledExecutor$Wor) line: 20"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Result\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForInnerClassMultiParameters() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("MyScheduledExecutor(Worker).doWorkParams(MyScheduledExecutor$Wor,MyScheduledExecutor$Ran) line: 23"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 2, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_Result\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + + public void testLinkNavigationTrueForLinksWithNoProperMethodSignature() throws Exception { + String projectName = "StackTest"; + IJavaProject project = createProject(projectName, "testfiles/AmbiguityTest/", JavaProjectHelper.JAVA_SE_1_8_EE_NAME, false); + waitForBuild(); + waitForJobs(); + consoleDocumentWithText("Sample.testBlank(Sample.java:40)"); + IHyperlink[] hyperlinks = fConsole.getHyperlinks(); + assertEquals("Wrong hyperlinks, listing all links: " + allLinks(), 1, hyperlinks.length); + String expectedText = "System.out.println(\"Expected_No_Signature\");"; + try { + for (IHyperlink hyperlink : hyperlinks) { + closeAllEditors(); + hyperlink.linkActivated(); + IEditorPart editor = waitForEditorOpen(); + String[] selectedText = new String[1]; + sync(() -> selectedText[0] = getSelectedText(editor)); + selectedText[0] = selectedText[0].trim(); + assertEquals("Wrong text selected after hyperlink navigation", expectedText, selectedText[0]); + + } + } finally { + closeAllEditors(); + boolean force = true; + project.getProject().delete(force, new NullProgressMonitor()); + } + } + private void waitForJobs() throws Exception { TestUtil.waitForJobs(getName(), 250, 10_000); } diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java index e0bf88d865..24bfe2e7a1 100644 --- a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/InnerClassTest.java @@ -19,11 +19,11 @@ public static void main(String[] ar) { } class innerL1 { - void check() { + void check2() { System.out.println("EXPECTED_INNERCLASS"); } class innerL2 { - void check2() { + void check4() { System.out.println("EXPECTED_INNER-INNER_CLASS"); } } diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/MyScheduledExecutor.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/MyScheduledExecutor.java new file mode 100644 index 0000000000..9db8c2a569 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/MyScheduledExecutor.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +public class MyScheduledExecutor { + public void executeTask() { + Wor worker = new Wor(); + } + class Worker { + public void doWork2(Wor w) { + + } + public void doWorkParams(Wor w,Ran r) { + System.out.println("Expected_Result"); + } + } + class Wor { + + } + class Ran { + + } +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/MyScheduledExecutor.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/MyScheduledExecutor.java new file mode 100644 index 0000000000..2af8104b36 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/MyScheduledExecutor.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +public class MyScheduledExecutor { + public void executeTask() { + Wor worker = new Wor(); + } + class Worker { + public void doWork(Wor w) { + System.out.println("Expected_Result"); + } + } + class Wor { + + } +} diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java index 6e19f1dea4..e52d37d128 100644 --- a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/a/Sample.java @@ -36,5 +36,7 @@ public void tes2() { public static void tesComplex(String[] x, java.net.URL[] sx) { System.out.println("Expected_One_normal_&_One_fully_qualified"); } - + public void testBlank() { + System.out.println("Expected_No_Signature"); + } } \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/InnerClassTest.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/InnerClassTest.java new file mode 100644 index 0000000000..b93df0192d --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/b/InnerClassTest.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * Test class + */ +class InnerClassTest { + public static void main(String[] ar) { + + } + class innerL1 { + void check1() { + System.out.println("EXPECTED_INNERCLASS"); + } + class innerL2 { + void check23() { + System.out.println("EXPECTED_INNER-INNER_CLASS"); + } + } + } +} + \ No newline at end of file diff --git a/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/InnerClassTest.java b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/InnerClassTest.java new file mode 100644 index 0000000000..e0bf88d865 --- /dev/null +++ b/org.eclipse.jdt.debug.tests/testfiles/AmbiguityTest/c/InnerClassTest.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +/** + * Test class + */ +class InnerClassTest { + public static void main(String[] ar) { + + } + class innerL1 { + void check() { + System.out.println("EXPECTED_INNERCLASS"); + } + class innerL2 { + void check2() { + System.out.println("EXPECTED_INNER-INNER_CLASS"); + } + } + } +} + \ No newline at end of file diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java index 06d579af05..c473072d05 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/console/JavaStackTraceHyperlink.java @@ -198,9 +198,16 @@ public IStatus runInUIThread(IProgressMonitor monitor) { if (generatedLink == null) { // Handles invalid links (without line number) return openClipboard(matches, line, typeName); } - int methodNameStartIndex = generatedLink.indexOf('.'); + int methodNameStartIndex; + if (generatedLink.indexOf('.') != generatedLink.lastIndexOf('.')) { + int lastEndIndex = generatedLink.lastIndexOf('('); + String tempSubstring = generatedLink.substring(0, lastEndIndex); + methodNameStartIndex = tempSubstring.lastIndexOf('.'); + } else { + methodNameStartIndex = generatedLink.indexOf('.'); + } int methodNameEndIndex = generatedLink.lastIndexOf(')'); - if (methodNameStartIndex != -1 && methodNameEndIndex != -1) { + if ((methodNameStartIndex != -1 && methodNameEndIndex != -1) && (methodNameStartIndex < methodNameEndIndex)) { List exactMatchesFiltered = new ArrayList<>(); String methodSignature = generatedLink.substring(methodNameStartIndex + 1, methodNameEndIndex + 1).replaceAll(" ", ""); //$NON-NLS-1$//$NON-NLS-2$ String methodNameExtracted = methodSignature.substring(0, methodSignature.indexOf('(')); @@ -213,10 +220,16 @@ public IStatus runInUIThread(IProgressMonitor monitor) { processSearchResult(exactMatchesFiltered.get(0), typeName, lineNumber); return Status.OK_STATUS; } else if (exactMatchesFiltered.size() > 1) { + for(Object res : exactMatchesFiltered) { + if (res instanceof IType type) { + if (type.getFullyQualifiedName().startsWith("bin.")) { //$NON-NLS-1$ + exactMatchesFiltered.remove(res); + } + } + } return openClipboard(exactMatchesFiltered, line, typeName); } } - return openClipboard(matches, line, typeName); } else { processSearchResult(source, typeName, lineNumber); @@ -288,18 +301,39 @@ private IStatus openClipboard(List results, int lineNumber, String type) * @return returns true if a found an exact method inside the class, or false if there's no matching methods */ private boolean extractFromInnerClassResults(IType[] innerClass, String methodSignature, String methodNameExtracted) throws JavaModelException { + int innerClasslevel = innerClassLevels(this.generatedLink); + int levelTravelled = 0; while (innerClass.length > 0) { for (IType innerType : innerClass) { if (innerClass.length > 0) { innerClass = innerType.getTypes(); } - if (extractFromResults(innerType, methodSignature, methodNameExtracted)) { + levelTravelled++; + if (extractFromResults(innerType, methodSignature, methodNameExtracted) && levelTravelled == innerClasslevel) { return true; } } } return false; } + + /** + * Checks the levels of inner class + * + * @param genLink + * Generated link + * @return returns count of levels + */ + + private int innerClassLevels(String genLink) { + int level = 0; + for (Character c : genLink.toCharArray()) { + if (c == '$') { + level++; + } + } + return level; + } /** * Checks if there's any matching methods for the given IType * @@ -313,38 +347,46 @@ private boolean extractFromInnerClassResults(IType[] innerClass, String methodSi * @return returns true if a found an exact method inside the class, or false if there's no matching methods */ private boolean extractFromResults(IType type, String methodSignature, String methodNameExtracted) throws JavaModelException { - IMethod[] methods = type.getMethods(); - for (IMethod method : methods) { - int indexOfClosing = method.toString().indexOf(')'); - int indexOfStart = method.toString().indexOf(method.getElementName()); - String methodName = method.toString().substring(indexOfStart, indexOfClosing + 1).replaceAll(" ", ""); //$NON-NLS-1$//$NON-NLS-2$ - int paramCount = methodSignature.substring(methodSignature.indexOf('(') - + 1, methodSignature.lastIndexOf(')')).split(",").length; //$NON-NLS-1$ - if (methodName.equals(methodSignature)) { - return true; - } else if (methodNameExtracted.equals(method.getElementName()) - && paramCount == method.getNumberOfParameters()) { - // Further mining from fully qualified parameter names in method signature - String methodSignatureGen = methodSignatureGenerator(method.getElementName(), methodName); - if (methodSignatureGen.equals(methodSignature)) { + boolean paramDetailsNotIncluded = methodSignature.substring(methodSignature.indexOf('(')).contains(".java:"); //$NON-NLS-1$ + try { + IMethod[] methods = type.getMethods(); + for (IMethod method : methods) { + if (paramDetailsNotIncluded && method.getElementName().equals(methodNameExtracted)) { return true; } - // If paramters includes innerclass - if (methodSignature.indexOf('$') != -1) { - String methodSignatureInnerClass = innerClassMethodSignatureGen(methodNameExtracted, methodSignature); - if (methodSignatureInnerClass.equals(methodSignatureGen)) { + String methodDetails = method.toString(); + int indexOfClosing = methodDetails.indexOf(')'); + int indexOfStart = methodDetails.indexOf(method.getElementName()); + String methodName = methodDetails.substring(indexOfStart, indexOfClosing + 1).replaceAll(" ", ""); //$NON-NLS-1$//$NON-NLS-2$ + int paramCount = methodSignature.substring(methodSignature.indexOf('(') + + 1, methodSignature.lastIndexOf(')')).split(",").length; //$NON-NLS-1$ + if (methodName.equals(methodSignature)) { + return true; + } else if (methodNameExtracted.equals(method.getElementName()) + && paramCount == method.getNumberOfParameters()) { + // Further mining from fully qualified parameter names in method signature + String methodSignatureGen = methodSignatureGenerator(method.getElementName(), methodName); + if (methodSignatureGen.equals(methodSignature)) { return true; } - String paramsExtracted = methodSignature.substring(methodSignature.indexOf('(')); - String param = paramsExtracted.substring(paramsExtracted.indexOf('$') + 1); - methodSignatureInnerClass.concat(param); - methodSignatureInnerClass.concat(")"); //$NON-NLS-1$ - if (methodSignatureInnerClass.toString().equals(methodSignatureGen)) { - return true; + // If paramters includes innerclass + if (methodSignature.indexOf('$') != -1) { + String methodSignatureInnerClass = innerClassMethodSignatureGen(methodNameExtracted, methodSignature); + if (methodSignatureInnerClass.equals(methodSignatureGen)) { + return true; + } + String paramsExtracted = methodSignature.substring(methodSignature.indexOf('(')); + String param = paramsExtracted.substring(paramsExtracted.indexOf('$') + 1); + methodSignatureInnerClass = methodSignatureInnerClass.concat(param); + if (methodSignatureInnerClass.equals(methodSignatureGen)) { + return true; + } } + return false; } - return false; } + } catch (Exception e) { + return false; } return false; }