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 b56331551f..581b556399 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 @@ -134,6 +134,15 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner { : HashTreePSet.empty(); } + @Override + protected boolean canReenter() { + // We might call the parsing logic again in the same thread, + // e.g. in order to determine "annotAttributes", getDeclaredMethods() is called, which + // calls ensureParsed(). + // Note: Other threads can't reenter, since our thread own the ParseLock monitor. + return true; + } + @Override protected boolean postCondition() { return signature != null && enclosingInfo != null; 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 6b442b3112..6784468646 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 @@ -163,7 +163,7 @@ abstract class GenericSigBase { @Override protected boolean postCondition() { - return (superItfs != null && superType != null || signature == null) && typeParameters != null; + return superItfs != null && (superType != null || signature == null) && typeParameters != null; } void setSuperInterfaces(List supers) { 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 7ed4f4d37f..e8790f292d 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 @@ -32,14 +32,22 @@ abstract class ParseLock { try { boolean success = doParse(); status = success ? ParseStatus.FULL : ParseStatus.FAILED; - this.status = status; finishParse(!success); } catch (Throwable t) { status = ParseStatus.FAILED; - this.status = status; LOG.error(t.toString(), t); finishParse(true); } + + // the status must be updated as last statement, so that + // other threads see the status FULL or FAILED only after finishParse() + // returns. Otherwise, some fields might not have been initialized. + // + // Note: the current thread might reenter the parsing logic through + // finishParse() -> ... -> ensureParsed(). In that case the status is still BEING_PARSED, + // and we don't call finishParse() again. See below for canReenter() + this.status = status; + assert status.isFinished : "Inconsistent status " + status; assert postCondition() : "Post condition not satisfied after parsing sig " + this; } else if (status == ParseStatus.BEING_PARSED && !canReenter()) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubTest.java new file mode 100644 index 0000000000..a3fdc175ab --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStubTest.java @@ -0,0 +1,28 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.symbols.internal.asm; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +import org.junit.jupiter.api.Test; +import org.pcollections.PSet; + +import net.sourceforge.pmd.lang.java.JavaParsingHelper; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.types.TypeSystem; + +class ClassStubTest { + // while parsing the annotation type, ClassStub's parseLock.ensureParsed() + // is called multiple times, reentering the parselock while the status is + // still BEING_PARSED. + @Test + void loadAndParseAnnotation() { + // class stub - annotation type + TypeSystem typeSystem = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader()); + JClassSymbol classSymbol = typeSystem.getClassSymbol("java.lang.Deprecated"); + PSet annotationAttributeNames = classSymbol.getAnnotationAttributeNames(); + assertFalse(annotationAttributeNames.isEmpty()); + } +}