From 3f697aff35cf7ddd1ce53df47d3e3e9eb5ebb87b Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 25 Oct 2024 18:07:16 +0200 Subject: [PATCH 01/17] [ant] Formatter: avoid reflective access to determine console encoding - for java 17+, there is public API to get the console encoding -> no problem - for older java versions, try to use system property sun.jnu.encoding if it exists - only then use the fall-backs with illegal reflective access to private fields/methods on java.io.Console - Also avoid using reflection utils from apache commons, instead use reflection directly. The illegal access warnings are then properly reported against our class net.sourceforge.pmd.ant.Formatter. Fixes #1860 --- .../net/sourceforge/pmd/ant/Formatter.java | 74 ++++++++++++------- .../java/net/sourceforge/pmd/dist/AntIT.java | 8 +- 2 files changed, 52 insertions(+), 30 deletions(-) diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java index f6ec0a9518..159f19d7e1 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.charset.Charset; @@ -21,8 +22,6 @@ import java.util.List; import java.util.Properties; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.reflect.FieldUtils; -import org.apache.commons.lang3.reflect.MethodUtils; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.Parameter; @@ -200,10 +199,12 @@ public class Formatter { if (console != null) { // Since Java 22, this returns a console even for redirected streams. // In that case, we need to check Console.isTerminal() + // https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/io/Console.html#isTerminal() // See: JLine As The Default Console Provider (JDK-8308591) try { - Boolean isTerminal = (Boolean) MethodUtils.invokeMethod(console, "isTerminal"); - if (!isTerminal) { + Method method = Console.class.getMethod("isTerminal"); + Object isTerminal = method.invoke(console); + if (isTerminal instanceof Boolean && !(Boolean) isTerminal) { // stop here, we don't have an interactive console. return null; } @@ -211,39 +212,58 @@ public class Formatter { // fall-through - we use a Java Runtime < 22. } + // Maybe this is Java17+? Then there will be a public method charset() + // https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Console.html#charset() try { - Object res = FieldUtils.readDeclaredField(console, "cs", true); - if (res instanceof Charset) { - return ((Charset) res).name(); + Method method = Console.class.getMethod("charset"); + Object charset = method.invoke(console); + if (charset instanceof Charset) { + return ((Charset) charset).name(); + } + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ignored) { + // fall-through + } + + { + // try to use the system property "sun.jnu.encoding", which is the platform encoding. + // this property is not specified and might not always be available, but it is for + // openjdk 11: https://github.com/openjdk/jdk11u/blob/cee8535a9d3de8558b4b5028d68e397e508bef71/src/java.base/share/native/libjava/System.c#L384 + // if it exists, we use it - this avoids illegal reflective access below. + String jnuEncoding = System.getProperty("sun.jnu.encoding"); + if (jnuEncoding != null) { + return jnuEncoding; + } + } + + // the following parts are accessing private/protected fields via reflection + // this should work with Java 8 and 11. With Java 11, you'll see warnings abouts + // illegal reflective access, see #1860. However, the access still works. + + // Fall-Back 1: private field "cs" in java.io.Console + try { + Field field = Console.class.getDeclaredField("cs"); + field.setAccessible(true); + Object csField = field.get(console); + if (csField instanceof Charset) { + return ((Charset) csField).name(); } } catch (IllegalArgumentException | ReflectiveOperationException ignored) { // fall-through } - // Maybe this is Java17+? Then there will be - // https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/Console.html#charset() - // instead of the field "cs". + // Fall-Back 2: private native method "encoding()" in java.io.Console try { - Method charsetMethod = Console.class.getDeclaredMethod("charset"); - Charset charset = (Charset) charsetMethod.invoke(console); - return charset.name(); - } catch (IllegalArgumentException | ReflectiveOperationException ignored) { + Method method = Console.class.getDeclaredMethod("encoding"); + method.setAccessible(true); + Object encoding = method.invoke(console); + if (encoding instanceof String) { + return (String) encoding; + } + } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ignored) { // fall-through } - return getNativeConsoleEncoding(); - } - return null; - } - - private static String getNativeConsoleEncoding() { - try { - Object res = MethodUtils.invokeStaticMethod(Console.class, "encoding"); - if (res instanceof String) { - return (String) res; - } - } catch (IllegalArgumentException | ReflectiveOperationException ignored) { - // fall-through } + // we couldn't determine the correct platform console encoding return null; } diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/AntIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/AntIT.java index d52070662f..6a0402d8a5 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/dist/AntIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/dist/AntIT.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.dist; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.containsStringIgnoringCase; +import static org.hamcrest.Matchers.not; import java.io.File; import java.io.IOException; @@ -40,9 +42,9 @@ class AntIT extends AbstractBinaryDistributionTest { ExecutionResult result = runAnt(antBasepath, pmdHome, antTestProjectFolder); result.assertExitCode(0) - .assertStdOut(containsString("BUILD SUCCESSFUL")); - // the no package rule - result.assertExitCode(0) + .assertStdOut(containsString("BUILD SUCCESSFUL")) + .assertStdOut(not(containsStringIgnoringCase("Illegal reflective access"))) // #1860 + // the no package rule .assertStdOut(containsString("NoPackage")); } From d68d6c57efa0d9e8d9613955b27a56662f6629cc Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 27 Oct 2024 15:53:00 +0100 Subject: [PATCH 02/17] Bump asm from 9.7 to 9.7.1 This enables support for Java 24 Refs #5154 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index e429008001..d91edae2cc 100644 --- a/pom.xml +++ b/pom.xml @@ -592,11 +592,11 @@ pmd-build-tools-config ${pmd.build-tools.version} - + org.ow2.asm asm - 9.7 + 9.7.1 @@ -828,7 +828,7 @@ org.ow2.asm asm - 9.7 + 9.7.1 org.pcollections From c81cd1d5203114ee42c781ce243ad044ff242944 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 03:31:36 +0000 Subject: [PATCH 03/17] Bump org.apache.maven.plugins:maven-assembly-plugin from 3.6.0 to 3.7.1 Bumps [org.apache.maven.plugins:maven-assembly-plugin](https://github.com/apache/maven-assembly-plugin) from 3.6.0 to 3.7.1. - [Release notes](https://github.com/apache/maven-assembly-plugin/releases) - [Commits](https://github.com/apache/maven-assembly-plugin/compare/maven-assembly-plugin-3.6.0...maven-assembly-plugin-3.7.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-assembly-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d91edae2cc..d09220f92b 100644 --- a/pom.xml +++ b/pom.xml @@ -179,7 +179,7 @@ org.apache.maven.plugins maven-assembly-plugin - 3.6.0 + 3.7.1 org.apache.maven.plugins From 69a92de52dddd720270f1a76ff27a9ccc5e8050c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 28 Oct 2024 22:10:21 -0300 Subject: [PATCH 04/17] Have pmd-xml Lexer in line with other antlr grammars - The package is no longer antlr4, but ast, as is in all other modules - We keep a deprecated proxy for backwards compatibility - We annotate the generated classes as such to ignore them from coverage reports --- pmd-xml/pom.xml | 22 +++++++++++++++++++ .../pmd/lang/xml/{antlr4 => ast}/XMLLexer.g4 | 0 .../pmd/lang/xml/antlr4/XMLLexer.java | 17 ++++++++++++++ .../pmd/lang/xml/cpd/XmlCpdLexer.java | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) rename pmd-xml/src/main/antlr4/net/sourceforge/pmd/lang/xml/{antlr4 => ast}/XMLLexer.g4 (100%) create mode 100644 pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java diff --git a/pmd-xml/pom.xml b/pmd-xml/pom.xml index 1d19200b86..676ba39759 100644 --- a/pmd-xml/pom.xml +++ b/pmd-xml/pom.xml @@ -45,6 +45,28 @@ + + + org.apache.maven.plugins + maven-antrun-plugin + + + antlr-cleanup + generate-sources + + run + + + + + + + + + + + + diff --git a/pmd-xml/src/main/antlr4/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.g4 b/pmd-xml/src/main/antlr4/net/sourceforge/pmd/lang/xml/ast/XMLLexer.g4 similarity index 100% rename from pmd-xml/src/main/antlr4/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.g4 rename to pmd-xml/src/main/antlr4/net/sourceforge/pmd/lang/xml/ast/XMLLexer.g4 diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java new file mode 100644 index 0000000000..2425c51b9f --- /dev/null +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java @@ -0,0 +1,17 @@ +package net.sourceforge.pmd.lang.xml.antlr4; + +import org.antlr.v4.runtime.CharStream; + +/** + * Backwards compatible bridge. The XMLLexer was moved to align it with other PMD modules. + * This class will be removed in PMD 8.0.0. + * Use {@link net.sourceforge.pmd.lang.xml.ast.XMLLexer} directly instead. + * + * @deprecated + */ +@Deprecated +public class XMLLexer extends net.sourceforge.pmd.lang.xml.ast.XMLLexer { + public XMLLexer(CharStream input) { + super(input); + } +} diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlCpdLexer.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlCpdLexer.java index 62b077ab4b..bf52b3d559 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlCpdLexer.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/cpd/XmlCpdLexer.java @@ -8,7 +8,7 @@ import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.Lexer; import net.sourceforge.pmd.cpd.impl.AntlrCpdLexer; -import net.sourceforge.pmd.lang.xml.antlr4.XMLLexer; +import net.sourceforge.pmd.lang.xml.ast.XMLLexer; /** *

Note: This class has been called XmlTokenizer in PMD 6

. From 7119424d963ecc91dcfcb8c89c23e673754a6659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Tue, 29 Oct 2024 16:56:30 -0300 Subject: [PATCH 05/17] Add license header --- .../java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java index 2425c51b9f..23221fe7cc 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java @@ -1,3 +1,6 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ package net.sourceforge.pmd.lang.xml.antlr4; import org.antlr.v4.runtime.CharStream; From 94095df8720322eb40fa9c7417cbbaea08af230d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Tue, 29 Oct 2024 17:21:24 -0300 Subject: [PATCH 06/17] Fix style issues --- .../main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java index 23221fe7cc..e86b9cb58e 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java @@ -1,6 +1,7 @@ /** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ + package net.sourceforge.pmd.lang.xml.antlr4; import org.antlr.v4.runtime.CharStream; From 6ae7404941cbe24d0309c37ad0b87e7f7e7ff68a Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 31 Oct 2024 12:17:27 +0100 Subject: [PATCH 07/17] [xml] XMLLexer - Update deprecation notice --- .../main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java index e86b9cb58e..ca52185930 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/antlr4/XMLLexer.java @@ -11,7 +11,7 @@ import org.antlr.v4.runtime.CharStream; * This class will be removed in PMD 8.0.0. * Use {@link net.sourceforge.pmd.lang.xml.ast.XMLLexer} directly instead. * - * @deprecated + * @deprecated since 7.8.0. Use {@link net.sourceforge.pmd.lang.xml.ast.XMLLexer} directly instead. */ @Deprecated public class XMLLexer extends net.sourceforge.pmd.lang.xml.ast.XMLLexer { From c079d8c544b94de5bff8b15c5cd72e62f7912037 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 31 Oct 2024 12:22:59 +0100 Subject: [PATCH 08/17] [doc] Update release notes (#5296) --- docs/pages/release_notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index bc1a36ec7a..53a0f49b5d 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,11 @@ This is a {{ site.pmd.release_type }} release. ### 🚨 API Changes +#### Deprecations +* pmd-xml + * {%jdoc xml::lang.xml.antlr4.XMLLexer %} is deprecated for removal. Use {%jdoc !!xml::lang.xml.ast.XMLLexer %} + instead (note different package `ast` instead of `antlr4`). + ### ✨ External Contributions {% endtocmaker %} From 5eaefd2841a20c9992a736ba69ae689b9d8533da Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 31 Oct 2024 15:06:32 +0100 Subject: [PATCH 09/17] [doc] Update release notes (#1860) --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 53a0f49b5d..3c14674217 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -15,6 +15,8 @@ This is a {{ site.pmd.release_type }} release. ### 🚀 New and noteworthy ### 🐛 Fixed Issues +* ant + * [#1860](https://github.com/pmd/pmd/issues/1860): \[ant] Reflective access warnings on java > 9 and java < 17 ### 🚨 API Changes From 3ed370f61d0579e33cf50852c2c59eeb9e94ae34 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 31 Oct 2024 15:07:25 +0100 Subject: [PATCH 10/17] Bump gems and bundler (#5301) - Bump bundler from 2.5.3 to 2.5.22 - Bump activesupport from 7.2.1 to 7.2.2 - Bump execjs from 2.9.1 to 2.10.0 - Bump faraday from 2.11.0 to 2.12.0 - Bump i18n from 1.14.5 to 1.14.6 - Bump json from 2.7.2 to 2.7.5 - Bump logger from 1.6.0 to 1.6.1 - Bump rexml from 3.3.6/3.3.8 to 3.3.9 - Bump rufus-scheduler from 3.9.2 to 3.9.2 - Fixes https://github.com/pmd/pmd/security/dependabot/69 - Fixes https://github.com/pmd/pmd/security/dependabot/70 --- Gemfile.lock | 10 +++++----- docs/Gemfile.lock | 20 +++++++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0ed3a0fa5f..1f7805ea50 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,7 +45,7 @@ GEM git (1.19.1) addressable (~> 2.8) rchardet (~> 1.8) - json (2.7.2) + json (2.7.5) kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) @@ -74,10 +74,10 @@ GEM raabro (1.4.0) racc (1.8.1) rchardet (1.8.0) - rexml (3.3.8) + rexml (3.3.9) rouge (4.4.0) - rufus-scheduler (3.9.1) - fugit (~> 1.1, >= 1.1.6) + rufus-scheduler (3.9.2) + fugit (~> 1.1, >= 1.11.1) safe_yaml (1.0.5) sawyer (0.9.2) addressable (>= 2.3.5) @@ -102,4 +102,4 @@ DEPENDENCIES safe_yaml BUNDLED WITH - 2.5.3 + 2.5.22 diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index a8439253e3..85d32da6ff 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,8 +1,9 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.2.1) + activesupport (7.2.2) base64 + benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) @@ -15,6 +16,7 @@ GEM addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) base64 (0.2.0) + benchmark (0.3.0) bigdecimal (3.1.8) coffee-script (2.4.1) coffee-script-source @@ -34,9 +36,10 @@ GEM ethon (0.16.0) ffi (>= 1.15.0) eventmachine (1.2.7) - execjs (2.9.1) - faraday (2.11.0) + execjs (2.10.0) + faraday (2.12.0) faraday-net_http (>= 2.0, < 3.4) + json logger faraday-net_http (3.3.0) net-http @@ -99,7 +102,7 @@ GEM activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.8.0) - i18n (1.14.5) + i18n (1.14.6) concurrent-ruby (~> 1.0) jekyll (3.10.0) addressable (~> 2.4) @@ -211,6 +214,7 @@ GEM gemoji (>= 3, < 5) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) + json (2.7.5) kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) @@ -219,7 +223,7 @@ GEM listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.6.0) + logger (1.6.1) mercenary (0.3.6) minima (2.5.1) jekyll (>= 3.5, < 5.0) @@ -240,8 +244,7 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.3.6) - strscan + rexml (3.3.9) rouge (3.30.0) rubyzip (2.3.2) safe_yaml (1.0.5) @@ -255,7 +258,6 @@ GEM faraday (>= 0.17.3, < 3) securerandom (0.3.1) simpleidn (0.2.3) - strscan (3.1.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) typhoeus (1.4.1) @@ -276,4 +278,4 @@ DEPENDENCIES webrick BUNDLED WITH - 2.5.3 + 2.5.22 From 36dfcf82112044442d083c15c778658a988fa949 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 03:08:41 +0000 Subject: [PATCH 11/17] Bump org.apache.maven.plugins:maven-clean-plugin from 3.3.2 to 3.4.0 Bumps [org.apache.maven.plugins:maven-clean-plugin](https://github.com/apache/maven-clean-plugin) from 3.3.2 to 3.4.0. - [Release notes](https://github.com/apache/maven-clean-plugin/releases) - [Commits](https://github.com/apache/maven-clean-plugin/compare/maven-clean-plugin-3.3.2...maven-clean-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-clean-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d09220f92b..915bd9c00e 100644 --- a/pom.xml +++ b/pom.xml @@ -202,7 +202,7 @@ org.apache.maven.plugins maven-clean-plugin - 3.3.2 + 3.4.0 From e15c05721eb1b202f41a8fa03db835becababff5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:23:58 +0100 Subject: [PATCH 12/17] Bump webrick from 1.8.2 to 1.9.0 in /docs in the all-gems group across 1 directory (#5308) Bump webrick in /docs in the all-gems group across 1 directory Bumps the all-gems group with 1 update in the /docs directory: [webrick](https://github.com/ruby/webrick). Updates `webrick` from 1.8.2 to 1.9.0 - [Release notes](https://github.com/ruby/webrick/releases) - [Commits](https://github.com/ruby/webrick/compare/v1.8.2...v1.9.0) --- updated-dependencies: - dependency-name: webrick dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-gems ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 85d32da6ff..c65178abcd 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -266,7 +266,7 @@ GEM concurrent-ruby (~> 1.0) unicode-display_width (1.8.0) uri (0.13.1) - webrick (1.8.2) + webrick (1.9.0) PLATFORMS x86_64-linux From a1996554d88d8a9109a44d31af9d62746f2e02ef Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 31 Oct 2024 17:04:19 +0100 Subject: [PATCH 13/17] [java] Add DeadlockTest for verifying #5293 - Improve logging for Parselock Refs #5293 --- .../java/symbols/internal/asm/ClassStub.java | 7 +- .../symbols/internal/asm/GenericSigBase.java | 8 +- .../java/symbols/internal/asm/ModuleStub.java | 2 +- .../java/symbols/internal/asm/ParseLock.java | 18 +++- .../pmd/lang/java/symbols/DeadlockTest.java | 86 +++++++++++++++++++ .../test/resources/simplelogger.properties | 5 ++ 6 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java create mode 100644 pmd-java/src/test/resources/simplelogger.properties diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java index 6a050ca5b3..c5337cf773 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java @@ -84,12 +84,7 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { this.resolver = resolver; this.names = new Names(internalName); - this.parseLock = new ParseLock() { - // note to devs: to debug the parsing logic you might have - // to replace the implementation of toString temporarily, - // otherwise an IDE could call toString just to show the item - // in the debugger view (which could cause parsing of the class file). - + this.parseLock = new ParseLock("ClassStub:" + internalName) { @Override protected boolean doParse() throws IOException { try (InputStream instream = loader.getInputStream()) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java index 6784468646..8ee5f39aaa 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java @@ -46,9 +46,9 @@ abstract class GenericSigBase { protected List typeParameters; private final ParseLock lock; - protected GenericSigBase(T ctx) { + protected GenericSigBase(T ctx, String parseLockName) { this.ctx = ctx; - this.lock = new ParseLock() { + this.lock = new ParseLock(parseLockName) { @Override protected boolean doParse() { GenericSigBase.this.doParse(); @@ -116,7 +116,7 @@ abstract class GenericSigBase { @Nullable String signature, // null if doesn't use generics in header @Nullable String superInternalName, // null if this is the Object class String[] interfaces) { - super(ctx); + super(ctx, "LazyClassSignature:" + ctx.getInternalName() + "[" + signature + "]"); this.signature = signature; this.rawItfs = CollectionUtil.map(interfaces, ctx.getResolver()::resolveFromInternalNameCannotFail); @@ -233,7 +233,7 @@ abstract class GenericSigBase { @Nullable String genericSig, @Nullable String[] exceptions, boolean skipFirstParam) { - super(ctx); + super(ctx, "LazyMethodType:" + (genericSig != null ? genericSig : descriptor)); this.signature = genericSig != null ? genericSig : descriptor; // generic signatures already omit the synthetic param this.skipFirstParam = skipFirstParam && genericSig == null; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ModuleStub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ModuleStub.java index 3db93664d3..1c0b33b0d2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ModuleStub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ModuleStub.java @@ -35,7 +35,7 @@ class ModuleStub implements JModuleSymbol, AsmStub, AnnotationOwner { this.resolver = resolver; this.moduleName = moduleName; - this.parseLock = new ParseLock() { + this.parseLock = new ParseLock("ModuleStub:" + moduleName) { @Override protected boolean doParse() throws IOException { try (InputStream instream = loader.getInputStream()) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ParseLock.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ParseLock.java index a7bfada5ea..a0150e00fe 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ParseLock.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ParseLock.java @@ -17,15 +17,30 @@ abstract class ParseLock { private static final Logger LOG = LoggerFactory.getLogger(ParseLock.class); private volatile ParseStatus status = ParseStatus.NOT_PARSED; + private final String name; + + protected ParseLock(String name) { + this.name = name; + } public void ensureParsed() { getFinalStatus(); } + private void logParseLockTrace(String prefix) { + if (LOG.isTraceEnabled()) { + LOG.trace("{} {}: {}", Thread.currentThread().getName(), String.format("%-15s", prefix), this); + } + } + private ParseStatus getFinalStatus() { ParseStatus status = this.status; if (!status.isFinished) { + logParseLockTrace("waiting on"); + synchronized (this) { + logParseLockTrace("locked"); + status = this.status; if (status == ParseStatus.NOT_PARSED) { this.status = ParseStatus.BEING_PARSED; @@ -54,6 +69,7 @@ abstract class ParseLock { throw new IllegalStateException("Thread is reentering the parse lock"); } } + logParseLockTrace("released"); } return status; } @@ -85,7 +101,7 @@ abstract class ParseLock { @Override public String toString() { - return "ParseLock{status=" + status + '}'; + return "ParseLock{name=" + name + ",status=" + status + '}'; } private enum ParseStatus { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java new file mode 100644 index 0000000000..25e3de1948 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java @@ -0,0 +1,86 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import net.sourceforge.pmd.lang.java.JavaParsingHelper; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; + +/** + * Tests to help analyze [java] Deadlock when executing PMD in multiple threads #5293. + * + * @see [java] Deadlock when executing PMD in multiple threads #5293 + */ +class DeadlockTest { + + abstract static class Outer implements GenericInterface, GenericClass> { + // must be a nested class, that is reusing the type param T of the outer class + abstract static class Inner { + Inner(Outer grid) { } + } + } + + static class GenericBaseClass { } + + interface GenericInterface { } + + abstract static class GenericClass extends GenericBaseClass> { } + + @Test + @Timeout(2) + void parseWithoutDeadlock() throws InterruptedException { + /* + Deadlock: + t1 -> locks parse for Outer.Inner and waits for parse lock for Outer + t2 -> locks parse for Outer, locks parse for GenericInterface and then waits for parse lock for Outer.Inner + + + In order to reproduce the deadlock reliably, add the following piece into ParseLock, just at the beginning + of the synchronized block (line 42): + + // t1 needs to wait after having the lock, so that t2 can go on and wait on the same lock + if (Thread.currentThread().getName().equals("t1") && this.toString().contains("LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner[ { + ASTCompilationUnit class1 = JavaParsingHelper.DEFAULT.parse( + "package net.sourceforge.pmd.lang.java.symbols;\n" + + "import net.sourceforge.pmd.lang.java.symbols.DeadlockTest.Outer;\n" + + " class Class1 {\n" + + " public static Outer.Inner newInner(Outer grid) {\n" + + " return null;\n" + + " }\n" + + " }\n" + ); + assertNotNull(class1); + }, "t1"); + + Thread t2 = new Thread(() -> { + ASTCompilationUnit class2 = JavaParsingHelper.DEFAULT.parse( + "package net.sourceforge.pmd.lang.java.symbols;\n" + + "import net.sourceforge.pmd.lang.java.symbols.DeadlockTest.Outer;\n" + + " class Class2 {\n" + + " protected Outer theOuter;\n" + + " }\n" + ); + assertNotNull(class2); + }, "t2"); + + t1.start(); + t2.start(); + + t1.join(); + t2.join(); + } +} diff --git a/pmd-java/src/test/resources/simplelogger.properties b/pmd-java/src/test/resources/simplelogger.properties new file mode 100644 index 0000000000..8a4eafe283 --- /dev/null +++ b/pmd-java/src/test/resources/simplelogger.properties @@ -0,0 +1,5 @@ +# +# BSD-style license; for more info see http://pmd.sourceforge.net/license.html +# + +#org.slf4j.simpleLogger.log.net.sourceforge.pmd.lang.java.symbols.internal.asm.ParseLock=trace From 1ee649442914de12f06de12f586cc647ad9a27a6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 4 Nov 2024 10:40:32 +0100 Subject: [PATCH 14/17] [java] Fix #5293: Parse number of type parameters eagerly When creating a LazyClassSignature or LazyMethodType, make sure to parse the number of type parameters eagerly, so that AstDisambiguationPass can get this number without triggering additional parsing. --- .../java/symbols/internal/asm/ClassStub.java | 4 +-- .../symbols/internal/asm/GenericSigBase.java | 20 ++++++++--- .../asm/GenericTypeParameterCounter.java | 36 +++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericTypeParameterCounter.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java index c5337cf773..15ae7b0535 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java @@ -310,9 +310,9 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { } @Override - public boolean isGeneric() { + public int getTypeParameterCount() { parseLock.ensureParsed(); - return signature.isGeneric(); + return signature.getTypeParameterCount(); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java index 8ee5f39aaa..eabeb13c5a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericSigBase.java @@ -81,7 +81,11 @@ abstract class GenericSigBase { protected abstract boolean postCondition(); - protected abstract boolean isGeneric(); + protected abstract int getTypeParameterCount(); + + protected boolean isGeneric() { + return getTypeParameterCount() > 0; + } public void setTypeParams(List tvars) { assert this.typeParameters == null : "Type params were already parsed for " + this; @@ -105,6 +109,7 @@ abstract class GenericSigBase { private static final String OBJECT_BOUND = ":" + OBJECT_SIG; private final @Nullable String signature; + private final int typeParameterCount; private @Nullable JClassType superType; private List superItfs; @@ -118,6 +123,7 @@ abstract class GenericSigBase { String[] interfaces) { super(ctx, "LazyClassSignature:" + ctx.getInternalName() + "[" + signature + "]"); this.signature = signature; + this.typeParameterCount = GenericTypeParameterCounter.determineTypeParameterCount(this.signature); this.rawItfs = CollectionUtil.map(interfaces, ctx.getResolver()::resolveFromInternalNameCannotFail); this.rawSuper = ctx.getResolver().resolveFromInternalNameCannotFail(superInternalName); @@ -157,8 +163,9 @@ abstract class GenericSigBase { } @Override - protected boolean isGeneric() { - return signature != null && TypeParamsParser.hasTypeParams(signature); + protected int getTypeParameterCount() { + // note: no ensureParsed() needed, the type parameters are counted eagerly + return typeParameterCount; } @Override @@ -206,6 +213,7 @@ abstract class GenericSigBase { static class LazyMethodType extends GenericSigBase implements TypeAnnotationReceiver { private final @NonNull String signature; + private final int typeParameterCount; private @Nullable TypeAnnotationSet receiverAnnotations; private List parameterTypes; @@ -235,6 +243,7 @@ abstract class GenericSigBase { boolean skipFirstParam) { super(ctx, "LazyMethodType:" + (genericSig != null ? genericSig : descriptor)); this.signature = genericSig != null ? genericSig : descriptor; + this.typeParameterCount = GenericTypeParameterCounter.determineTypeParameterCount(genericSig); // generic signatures already omit the synthetic param this.skipFirstParam = skipFirstParam && genericSig == null; this.rawExceptions = exceptions; @@ -288,8 +297,9 @@ abstract class GenericSigBase { @Override - protected boolean isGeneric() { - return TypeParamsParser.hasTypeParams(signature); + protected int getTypeParameterCount() { + // note: no ensureParsed() needed, the type parameters are counted eagerly + return typeParameterCount; } void setParameterTypes(List params) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericTypeParameterCounter.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericTypeParameterCounter.java new file mode 100644 index 0000000000..28f309c43b --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/GenericTypeParameterCounter.java @@ -0,0 +1,36 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.asm; + +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.signature.SignatureVisitor; + +class GenericTypeParameterCounter extends SignatureVisitor { + private int count; + + GenericTypeParameterCounter() { + super(AsmSymbolResolver.ASM_API_V); + } + + @Override + public void visitFormalTypeParameter(String name) { + count++; + } + + public int getCount() { + return count; + } + + static int determineTypeParameterCount(String signature) { + if (signature == null) { + return 0; + } + + SignatureReader signatureReader = new SignatureReader(signature); + GenericTypeParameterCounter counter = new GenericTypeParameterCounter(); + signatureReader.accept(counter); + return counter.getCount(); + } +} From 733ac4bba04520af86097047c56bba1e9b8b5900 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 4 Nov 2024 11:21:25 +0100 Subject: [PATCH 15/17] [doc] Update release notes (#5293) --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 3c14674217..3e50f0fc86 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -17,6 +17,8 @@ This is a {{ site.pmd.release_type }} release. ### 🐛 Fixed Issues * ant * [#1860](https://github.com/pmd/pmd/issues/1860): \[ant] Reflective access warnings on java > 9 and java < 17 +* java + * [#5293](https://github.com/pmd/pmd/issues/5293): \[java] Deadlock when executing PMD in multiple threads ### 🚨 API Changes From 9dcb697f13549dab1bce1dac0d4c9b7fa5cfe215 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Nov 2024 12:38:11 +0100 Subject: [PATCH 16/17] Improve DeadlockTest --- .../pmd/lang/java/symbols/DeadlockTest.java | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java index 25e3de1948..15358be481 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/DeadlockTest.java @@ -4,12 +4,19 @@ package net.sourceforge.pmd.lang.java.symbols; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import net.sourceforge.pmd.lang.java.JavaParsingHelper; +import net.sourceforge.pmd.lang.java.ast.ASTClassType; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; /** @@ -38,7 +45,21 @@ class DeadlockTest { /* Deadlock: t1 -> locks parse for Outer.Inner and waits for parse lock for Outer + ├─ t1 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner[Ljava/lang/Object;],status=NOT_PARSED} + └─ t1 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner[Ljava/lang/Object;],status=NOT_PARSED} + └─ t1 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass;>;],status=BEING_PARSED} t2 -> locks parse for Outer, locks parse for GenericInterface and then waits for parse lock for Outer.Inner + ├─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass;>;],status=NOT_PARSED} + └─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass;>;],status=NOT_PARSED} + ├─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest[null],status=NOT_PARSED} + ├─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest[null],status=NOT_PARSED} + ├─ t2 released : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest[null],status=FULL} + ├─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass;>;],status=BEING_PARSED} + ├─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass;>;],status=BEING_PARSED} + ├─ t2 released : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer[Ljava/lang/Object;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericInterface;Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass;>;],status=BEING_PARSED} + └─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass[Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericBaseClass;>;],status=NOT_PARSED} + ├─ t2 locked : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericClass[Lnet/sourceforge/pmd/lang/java/symbols/DeadlockTest$GenericBaseClass;>;],status=NOT_PARSED} + └─ t2 waiting on : ParseLock{name=LazyClassSignature:net/sourceforge/pmd/lang/java/symbols/DeadlockTest$Outer$Inner[Ljava/lang/Object;],status=NOT_PARSED} In order to reproduce the deadlock reliably, add the following piece into ParseLock, just at the beginning @@ -51,20 +72,50 @@ class DeadlockTest { } catch (InterruptedException ignored) { } } + + And then, introduce a bug again. One way to make the test fail is: + Comment out the method "public int getTypeParameterCount()", so that it is inherited again. + Add the following method: + @Override + public boolean isGeneric() { + parseLock.ensureParsed(); + return signature.isGeneric(); + } */ + List exceptions = new ArrayList<>(); + Thread.UncaughtExceptionHandler exceptionHandler = (t, e) -> { + exceptions.add(e); + e.printStackTrace(); + }; + Thread t1 = new Thread(() -> { ASTCompilationUnit class1 = JavaParsingHelper.DEFAULT.parse( "package net.sourceforge.pmd.lang.java.symbols;\n" + "import net.sourceforge.pmd.lang.java.symbols.DeadlockTest.Outer;\n" + " class Class1 {\n" - + " public static Outer.Inner newInner(Outer grid) {\n" + + " public static Outer.Inner newInner(Outer grid) {\n" + " return null;\n" + " }\n" + " }\n" ); assertNotNull(class1); + + // Outer.Inner = return type of method "newInner" + List classTypes = class1.descendants(ASTClassType.class).toList(); + ASTClassType outerInner = classTypes.get(0); + assertGenericClassType(outerInner, "Inner", "X", "T"); + + // Outer = qualifier of Outer.Inner + ASTClassType outer = classTypes.get(1); + assertEquals("Outer", outer.getSimpleName()); + assertNull(outer.getTypeArguments()); + + // Outer = formal parameter type of method newInner + ASTClassType outerFormalParam = classTypes.get(3); + assertGenericClassType(outerFormalParam, "Outer", "X", "T"); }, "t1"); + t1.setUncaughtExceptionHandler(exceptionHandler); Thread t2 = new Thread(() -> { ASTCompilationUnit class2 = JavaParsingHelper.DEFAULT.parse( @@ -75,12 +126,32 @@ class DeadlockTest { + " }\n" ); assertNotNull(class2); + + // Outer = type of field "theOuter" + ASTClassType firstClassType = class2.descendants(ASTClassType.class).first(); + assertNotNull(firstClassType); + assertGenericClassType(firstClassType, "Outer", "M", "T"); }, "t2"); + t2.setUncaughtExceptionHandler(exceptionHandler); t1.start(); t2.start(); t1.join(); t2.join(); + + assertAll(exceptions.stream() + .map(e -> () -> { + throw e; + })); + } + + private static void assertGenericClassType(ASTClassType classType, String simpleName, String actualTypeParamName, String originalTypeParamName) { + assertEquals(simpleName, classType.getSimpleName()); + assertEquals(1, classType.getTypeArguments().size()); + assertEquals(actualTypeParamName, ((ASTClassType) classType.getTypeArguments().get(0)).getSimpleName()); + JTypeParameterOwnerSymbol symbol = (JTypeParameterOwnerSymbol) classType.getTypeMirror().getSymbol(); + assertEquals(1, symbol.getTypeParameterCount()); + assertEquals(originalTypeParamName, symbol.getTypeParameters().get(0).getName()); } } From ca208d22416dc2abec528208347fca1de34e3de9 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Nov 2024 12:40:18 +0100 Subject: [PATCH 17/17] Bump maven-pmd-plugin from 3.24.0 to 3.26.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 915bd9c00e..dec91f16ef 100644 --- a/pom.xml +++ b/pom.xml @@ -101,7 +101,7 @@ 3.2.5 10.18.1 3.5.0 - 3.24.0 + 3.26.0 1.10.14 3.6.3 4.9.3