/
niceSOFT
/
openjdk21
Обзор
Документация
Войти
/
niceSOFT
/
openjdk21
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/jdk/internal/classfile/impl/ClassHierarchyImpl.java
255 строк
13 KB
Roger Riggs
8313312: Add missing classpath exception copyright header
24 авг 2023, 21:05
24 авг 2023, 21:05
4729fad
Код
Авторство
О чём код?
/* * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ package jdk.internal.classfile.impl; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.lang.constant.ClassDesc; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Supplier; import jdk.internal.classfile.ClassHierarchyResolver; import static java.lang.constant.ConstantDescs.CD_Object; import static jdk.internal.classfile.Classfile.*; /** * Class hierarchy resolution framework is answering questions about classes assignability, common classes ancestor and whether the class represents an interface. * All the requests are handled without class loading nor full verification, optionally with incomplete dependencies and with focus on maximum performance. * */ public final class ClassHierarchyImpl { public record ClassHierarchyInfoImpl(ClassDesc superClass, boolean isInterface) implements ClassHierarchyResolver.ClassHierarchyInfo { static final ClassHierarchyResolver.ClassHierarchyInfo OBJECT_INFO = new ClassHierarchyInfoImpl(null, false); } public static final ClassHierarchyResolver DEFAULT_RESOLVER = ClassHierarchyResolver .ofResourceParsing(ResourceParsingClassHierarchyResolver.SYSTEM_STREAM_PROVIDER) .orElse(new ClassLoadingClassHierarchyResolver(ClassLoadingClassHierarchyResolver.SYSTEM_CLASS_PROVIDER)) .cached(new Supplier<>() { @Override public Map<ClassDesc, ClassHierarchyResolver.ClassHierarchyInfo> get() { return new ConcurrentHashMap<>(); } }); private final ClassHierarchyResolver resolver; /** * Public constructor of <code>ClassHierarchyImpl</code> accepting instances of <code>ClassHierarchyInfoResolver</code> to resolve individual class streams. * @param classHierarchyResolver <code>ClassHierarchyInfoResolver</code> instance */ public ClassHierarchyImpl(ClassHierarchyResolver classHierarchyResolver) { this.resolver = classHierarchyResolver; } private ClassHierarchyInfoImpl resolve(ClassDesc classDesc) { var res = resolver.getClassInfo(classDesc); if (res != null) return (ClassHierarchyInfoImpl) res; throw new IllegalArgumentException("Could not resolve class " + classDesc.displayName()); } /** * Method answering question whether given class is an interface, * responding without the class stream resolution and parsing is preferred in case the interface status is known from previous activities. * @param classDesc class path in form of <package>/<class_name>.class * @return true if the given class name represents an interface */ public boolean isInterface(ClassDesc classDesc) { return resolve(classDesc).isInterface(); } /** * Method resolving common ancestor of two classes * @param symbol1 first class descriptor * @param symbol2 second class descriptor * @return common ancestor class name or <code>null</code> if it could not be identified */ public ClassDesc commonAncestor(ClassDesc symbol1, ClassDesc symbol2) { //calculation of common ancestor is a robust (yet fast) way to decide about assignability in incompletely resolved class hierarchy //exact order of symbol loops is critical for performance of the above isAssignableFrom method, so standard situations are resolved in linear time //this method returns null if common ancestor could not be identified if (isInterface(symbol1) || isInterface(symbol2)) return CD_Object; for (var s1 = symbol1; s1 != null; s1 = resolve(s1).superClass()) { for (var s2 = symbol2; s2 != null; s2 = resolve(s2).superClass()) { if (s1.equals(s2)) return s1; } } return null; } public boolean isAssignableFrom(ClassDesc thisClass, ClassDesc fromClass) { //extra check if fromClass is an interface is necessay to handle situation when thisClass might not been fully resolved and so it is potentially an unidentified interface //this special corner-case handling has been added based on better success rate of constructing stack maps with simulated broken resulution of classes and interfaces if (isInterface(fromClass)) return resolve(thisClass).superClass() == null; //regular calculation of assignability is based on common ancestor calculation var anc = commonAncestor(thisClass, fromClass); //if common ancestor does not exist (as the class hierarchy could not be fully resolved) we optimistically assume the classes might be accessible //if common ancestor is equal to thisClass then the classes are clearly accessible //if other common ancestor is calculated (which works even when their grand-parents could not be resolved) then it is clear that thisClass could not be asigned from fromClass return anc == null || thisClass.equals(anc); } public static final class CachedClassHierarchyResolver implements ClassHierarchyResolver { //this instance should leak out, appears only in cache in order to utilize Map.computeIfAbsent private static final ClassHierarchyResolver.ClassHierarchyInfo NOPE = new ClassHierarchyInfoImpl(null, true); private final Map<ClassDesc, ClassHierarchyInfo> resolvedCache; private final Function<ClassDesc, ClassHierarchyInfo> delegateFunction; public CachedClassHierarchyResolver(ClassHierarchyResolver delegate, Map<ClassDesc, ClassHierarchyInfo> resolvedCache) { this.resolvedCache = resolvedCache; this.delegateFunction = new Function<>() { @Override public ClassHierarchyInfo apply(ClassDesc classDesc) { var ret = delegate.getClassInfo(classDesc); return ret == null ? NOPE : ret; } }; } @Override public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { var ret = resolvedCache.computeIfAbsent(classDesc, delegateFunction); return ret == NOPE ? null : ret; } } public static final class ResourceParsingClassHierarchyResolver implements ClassHierarchyResolver { public static final Function<ClassDesc, InputStream> SYSTEM_STREAM_PROVIDER = new Function<>() { @Override public InputStream apply(ClassDesc cd) { return ClassLoader.getSystemClassLoader().getResourceAsStream(Util.toInternalName(cd) + ".class"); } }; private final Function<ClassDesc, InputStream> streamProvider; public ResourceParsingClassHierarchyResolver(Function<ClassDesc, InputStream> classStreamProvider) { this.streamProvider = classStreamProvider; } // resolve method looks for the class file using <code>ClassStreamResolver</code> instance and tries to briefly scan it just for minimal information necessary // minimal information includes: identification of the class as interface, obtaining its superclass name and identification of all potential interfaces (to avoid unnecessary future resolutions of them) // empty ClInfo is stored in case of an exception to avoid repeated scanning failures @Override public ClassHierarchyResolver.ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { var ci = streamProvider.apply(classDesc); if (ci == null) return null; try (var in = new DataInputStream(new BufferedInputStream(ci))) { in.skipBytes(8); int cpLength = in.readUnsignedShort(); String[] cpStrings = new String[cpLength]; int[] cpClasses = new int[cpLength]; for (int i = 1; i < cpLength; i++) { int tag; switch (tag = in.readUnsignedByte()) { case TAG_UTF8 -> cpStrings[i] = in.readUTF(); case TAG_CLASS -> cpClasses[i] = in.readUnsignedShort(); case TAG_STRING, TAG_METHODTYPE, TAG_MODULE, TAG_PACKAGE -> in.skipBytes(2); case TAG_METHODHANDLE -> in.skipBytes(3); case TAG_INTEGER, TAG_FLOAT, TAG_FIELDREF, TAG_METHODREF, TAG_INTERFACEMETHODREF, TAG_NAMEANDTYPE, TAG_CONSTANTDYNAMIC, TAG_INVOKEDYNAMIC -> in.skipBytes(4); case TAG_LONG, TAG_DOUBLE -> { in.skipBytes(8); i++; } default -> throw new IllegalStateException("Bad tag (" + tag + ") at index (" + i + ")"); } } boolean isInterface = (in.readUnsignedShort() & ACC_INTERFACE) != 0; in.skipBytes(2); int superIndex = in.readUnsignedShort(); var superClass = superIndex > 0 ? ClassDesc.ofInternalName(cpStrings[cpClasses[superIndex]]) : null; return new ClassHierarchyInfoImpl(superClass, isInterface); } catch (IOException ioe) { throw new UncheckedIOException(ioe); } } } public static final class StaticClassHierarchyResolver implements ClassHierarchyResolver { private final Map<ClassDesc, ClassHierarchyInfo> map; public StaticClassHierarchyResolver(Collection<ClassDesc> interfaceNames, Map<ClassDesc, ClassDesc> classToSuperClass) { map = HashMap.newHashMap(interfaceNames.size() + classToSuperClass.size() + 1); map.put(CD_Object, ClassHierarchyInfoImpl.OBJECT_INFO); for (var e : classToSuperClass.entrySet()) map.put(e.getKey(), ClassHierarchyInfo.ofClass(e.getValue())); for (var i : interfaceNames) map.put(i, ClassHierarchyInfo.ofInterface()); } @Override public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) { return map.get(classDesc); } } public static final class ClassLoadingClassHierarchyResolver implements ClassHierarchyResolver { public static final Function<ClassDesc, Class<?>> SYSTEM_CLASS_PROVIDER = new Function<>() { @Override public Class<?> apply(ClassDesc cd) { try { return Class.forName(Util.toBinaryName(cd.descriptorString()), false, ClassLoader.getSystemClassLoader()); } catch (ClassNotFoundException ex) { return null; } } }; private final Function<ClassDesc, Class<?>> classProvider; public ClassLoadingClassHierarchyResolver(Function<ClassDesc, Class<?>> classProvider) { this.classProvider = classProvider; } @Override public ClassHierarchyInfo getClassInfo(ClassDesc cd) { if (!cd.isClassOrInterface()) return null; if (cd.equals(CD_Object)) return ClassHierarchyInfo.ofClass(null); var cl = classProvider.apply(cd); if (cl == null) { return null; } return cl.isInterface() ? ClassHierarchyInfo.ofInterface() : ClassHierarchyInfo.ofClass(cl.getSuperclass().describeConstable().orElseThrow()); } } }