Created
April 15, 2020 13:33
-
-
Save DasBrain/8d50e02e35654870e2c2c00bf3f79954 to your computer and use it in GitHub Desktop.
Classes that have a public void close() but don't implement AutoCloseable
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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