[java] Fix intermittent NPE ClassSub.enclosingInfo is null

Refs #4757
This commit is contained in:
Andreas Dangel
2024-02-24 21:09:57 +01:00
parent ce347bd736
commit a8c41a5096
4 changed files with 48 additions and 3 deletions

View File

@ -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;

View File

@ -163,7 +163,7 @@ abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
@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<JClassType> supers) {

View File

@ -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()) {

View File

@ -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<String> annotationAttributeNames = classSymbol.getAnnotationAttributeNames();
assertFalse(annotationAttributeNames.isEmpty());
}
}