Skip to content

Instantly share code, notes, and snippets.

@DasBrain
Created April 15, 2020 13:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DasBrain/8d50e02e35654870e2c2c00bf3f79954 to your computer and use it in GitHub Desktop.
Save DasBrain/8d50e02e35654870e2c2c00bf3f79954 to your computer and use it in GitHub Desktop.
Classes that have a public void close() but don't implement AutoCloseable
com/sun/jdi/connect/spi/Connection
com/sun/jndi/dns/BaseNameClassPairEnumeration
com/sun/jndi/dns/DnsClient
com/sun/jndi/dns/DnsContext
com/sun/jndi/dns/Resolver
com/sun/jndi/ldap/AbstractLdapNamingEnumeration
com/sun/jndi/ldap/LdapCtx
com/sun/jndi/ldap/LdapReferralContext
com/sun/jndi/ldap/LdapSchemaCtx
com/sun/jndi/ldap/ext/StartTlsResponseImpl
com/sun/jndi/rmi/registry/BindingEnumeration
com/sun/jndi/rmi/registry/NameClassPairEnumeration
com/sun/jndi/rmi/registry/RegistryContext
com/sun/jndi/toolkit/dir/ContextEnumerator
com/sun/jndi/toolkit/dir/HierMemDirCtx
com/sun/jndi/toolkit/dir/HierMemDirCtx$BaseFlatNames
com/sun/jndi/toolkit/dir/LazySearchEnumerationImpl
com/sun/jndi/toolkit/url/GenericURLContext
com/sun/media/sound/AudioFloatFormatConverter$AudioFloatInputStreamChannelMixer
com/sun/media/sound/AudioFloatFormatConverter$AudioFloatInputStreamResampler
com/sun/media/sound/AudioFloatInputStream
com/sun/media/sound/AudioFloatInputStream$BytaArrayAudioFloatInputStream
com/sun/media/sound/AudioFloatInputStream$DirectAudioFloatInputStream
com/sun/media/sound/ModelAbstractOscillator
com/sun/media/sound/ModelDirector
com/sun/media/sound/ModelOscillatorStream
com/sun/media/sound/ModelStandardDirector
com/sun/media/sound/ModelStandardIndexedDirector
com/sun/media/sound/RIFFWriter$RandomAccessByteWriter
com/sun/media/sound/RIFFWriter$RandomAccessFileWriter
com/sun/media/sound/RIFFWriter$RandomAccessWriter
com/sun/media/sound/SoftAbstractResampler$ModelAbstractResamplerStream
com/sun/media/sound/SoftMainMixer
com/sun/media/sound/SoftMixingDataLine$AudioFloatInputStreamResampler
com/sun/media/sound/SoftMixingMainMixer
com/sun/media/sound/SoftMixingSourceDataLine$NonBlockingFloatInputStream
com/sun/naming/internal/VersionHelper$InputStreamEnumeration
com/sun/org/apache/xerces/internal/impl/XMLStreamFilterImpl
com/sun/org/apache/xerces/internal/impl/XMLStreamReaderImpl
com/sun/org/apache/xerces/internal/xinclude/XIncludeTextReader
com/sun/org/apache/xml/internal/serializer/EmptySerializer
com/sun/org/apache/xml/internal/serializer/SerializationHandler
com/sun/org/apache/xml/internal/serializer/SerializerBase
com/sun/org/apache/xml/internal/serializer/ToHTMLSAXHandler
com/sun/org/apache/xml/internal/serializer/ToUnknownStream
com/sun/org/apache/xml/internal/serializer/WriterChain
com/sun/tools/javac/api/JavacTaskPool$ReusableContext$ReusableJavaCompiler
com/sun/tools/javac/file/JavacFileManager$1
com/sun/tools/javac/file/JavacFileManager$ArchiveContainer
com/sun/tools/javac/file/JavacFileManager$Container
com/sun/tools/javac/file/JavacFileManager$DirectoryContainer
com/sun/tools/javac/file/JavacFileManager$JRTImageContainer
com/sun/tools/javac/file/Locations
com/sun/tools/javac/main/JavaCompiler
com/sun/tools/javac/processing/JavacProcessingEnvironment$DiscoveredProcessors
com/sun/tools/javac/processing/JavacProcessingEnvironment$ServiceIterator
com/sun/tools/jdi/SharedMemoryConnection
com/sun/tools/jdi/SocketConnection
com/sun/tools/sjavac/PubApiExtractor
com/sun/xml/internal/stream/Entity$ScannedEntity
com/sun/xml/internal/stream/XMLEventReaderImpl
com/sun/xml/internal/stream/writers/XMLDOMWriterImpl
com/sun/xml/internal/stream/writers/XMLEventWriterImpl
com/sun/xml/internal/stream/writers/XMLStreamWriterImpl
java/awt/SplashScreen
java/util/logging/ConsoleHandler
java/util/logging/FileHandler
java/util/logging/Handler
java/util/logging/MemoryHandler
java/util/logging/SocketHandler
java/util/logging/StreamHandler
javax/naming/Context
javax/naming/InitialContext
javax/naming/NamingEnumeration
javax/naming/directory/BasicAttribute$ValuesEnumImpl
javax/naming/directory/BasicAttributes$AttrEnumImpl
javax/naming/directory/BasicAttributes$IDEnumImpl
javax/naming/ldap/StartTlsResponse
javax/naming/spi/ContinuationContext
javax/smartcardio/CardChannel
javax/sql/PooledConnection
javax/swing/ProgressMonitor
javax/swing/text/rtf/RTFReader$AttributeTrackingDestination
javax/swing/text/rtf/RTFReader$ColortblDestination
javax/swing/text/rtf/RTFReader$Destination
javax/swing/text/rtf/RTFReader$DiscardingDestination
javax/swing/text/rtf/RTFReader$FonttblDestination
javax/swing/text/rtf/RTFReader$StylesheetDestination
javax/swing/text/rtf/RTFReader$StylesheetDestination$StyleDefiningDestination
javax/swing/text/rtf/RTFReader$TextHandlingDestination
javax/xml/stream/XMLEventReader
javax/xml/stream/XMLEventWriter
javax/xml/stream/XMLStreamReader
javax/xml/stream/XMLStreamWriter
javax/xml/stream/util/EventReaderDelegate
javax/xml/stream/util/StreamReaderDelegate
jdk/internal/jrtfs/ExplodedImage
jdk/internal/util/xml/XMLStreamWriter
jdk/internal/util/xml/impl/XMLStreamWriterImpl
jdk/jfr/internal/consumer/ChunkParser
jdk/jshell/SourceCodeAnalysisImpl
jdk/tools/jlink/internal/Archive
jdk/tools/jlink/internal/DirArchive
jdk/tools/jlink/internal/JarArchive
jdk/tools/jlink/internal/JmodArchive
org/graalvm/compiler/code/CompilationResult
org/graalvm/compiler/code/DataSection
org/graalvm/compiler/debug/DiagnosticsOutputDirectory
org/w3c/dom/html/HTMLDocument
sun/awt/image/ImageDecoder
sun/jvm/hotspot/debugger/DataSource
sun/jvm/hotspot/debugger/InputLexer
sun/jvm/hotspot/debugger/MappedByteBufferDataSource
sun/jvm/hotspot/debugger/RandomAccessFileDataSource
sun/jvm/hotspot/debugger/posix/AddressDataSource
sun/jvm/hotspot/debugger/posix/elf/ELFFile
sun/jvm/hotspot/debugger/posix/elf/ELFFileParser$ELFFileImpl
sun/jvm/hotspot/debugger/win32/coff/COFFFile
sun/jvm/hotspot/debugger/win32/coff/COFFFileParser$COFFFileImpl
sun/jvm/hotspot/debugger/windbg/AddressDataSource
sun/jvm/hotspot/debugger/windbg/DLL
sun/net/ProgressSource
sun/net/httpserver/ExchangeImpl
sun/net/www/URLConnection
sun/rmi/log/ReliableLog
sun/rmi/runtime/Log$InternalStreamHandler
sun/rmi/transport/Connection
sun/rmi/transport/tcp/TCPConnection
sun/security/smartcardio/ChannelImpl
sun/tools/java/ClassPath
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
record ClassInfo(String name, String parent, Set<String> interfaces, boolean hasClose) {}
public final class ScanAutoCloseable {
public static void main(String[] args) throws Throwable {
var fs = FileSystems.getFileSystem(URI.create("jrt:/"));
BiPredicate<Path, BasicFileAttributes> matcher = (p, a) -> p.getFileName() != null && p.getFileName().toString().endsWith(".class");
Map<String, ClassInfo> classInfo;
try (var classes = Files.find(fs.getPath("/"), 20, matcher)) {
classInfo = classes.filter(f -> !f.endsWith("module-info.class"))
.map(p -> readClassInfo(p))
.collect(Collectors.toUnmodifiableMap(ClassInfo::name, Function.identity()));
}
var checker = new ClassHierarchyCheck(classInfo);
classInfo.values().stream()
.filter(ClassInfo::hasClose)
.map(ClassInfo::name)
.filter(c -> !checker.implAutoClose(c))
.sorted()
.forEach(System.out::println);
}
private static ClassInfo readClassInfo(Path path) {
try {
ClassReader cr = new ClassReader(Files.readAllBytes(path));
ClassParser cp = new ClassParser();
cr.accept(cp, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return cp.classInfo();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
class ClassHierarchyCheck {
private final Map<String, Boolean> cache = new HashMap<>();
private final Map<String, ClassInfo> classInfo;
public ClassHierarchyCheck(Map<String, ClassInfo> classInfo) {
this.classInfo = classInfo;
}
// Not strictly side-effect free. But it's only caching.
boolean implAutoClose(String className) {
// computeIfAbsent would throw a concurrent modification exception
Boolean result = cache.get(className);
if (result == null) {
boolean res = implAutoClose0(className);
cache.put(className, res);
return res;
}
return result;
}
private boolean implAutoClose0(String className) {
if (className == null) return false;
if (className.equals("java/lang/AutoCloseable")) return true;
ClassInfo ci = classInfo.get(className);
if (implAutoClose(ci.parent())) return true;
return ci.interfaces().stream().anyMatch(this::implAutoClose);
}
}
class ClassParser extends ClassVisitor {
String name;
String parent;
String[] interfaces;
boolean hasClose;
ClassParser() {
super(Opcodes.ASM8);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.name = name;
this.parent = superName;
this.interfaces = interfaces;
super.visit(version, access, name, signature, superName, interfaces);
}
private static final int ACC_PUBLIC_STATIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
if ((access & ACC_PUBLIC_STATIC) == Opcodes.ACC_PUBLIC &&
name.equals("close") && descriptor.equals("()V")) {
hasClose = true;
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
ClassInfo classInfo() {
return new ClassInfo(name, parent, Set.of(interfaces), hasClose);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment