mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8304425: ClassHierarchyResolver from Reflection
Reviewed-by: asotona
This commit is contained in:
parent
79a4ac791c
commit
ac3ce2bf75
10 changed files with 373 additions and 98 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
|
@ -26,12 +26,19 @@ package jdk.internal.classfile;
|
|||
|
||||
import java.io.InputStream;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import jdk.internal.classfile.impl.Util;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.classfile.impl.ClassHierarchyImpl;
|
||||
import jdk.internal.classfile.impl.ClassHierarchyImpl.ClassLoadingClassHierarchyResolver;
|
||||
import jdk.internal.classfile.impl.ClassHierarchyImpl.StaticClassHierarchyResolver;
|
||||
import jdk.internal.classfile.impl.Util;
|
||||
|
||||
import static java.lang.constant.ConstantDescs.CD_Object;
|
||||
|
||||
/**
|
||||
* Provides class hierarchy information for generating correct stack maps
|
||||
|
@ -41,18 +48,14 @@ import jdk.internal.classfile.impl.ClassHierarchyImpl;
|
|||
public interface ClassHierarchyResolver {
|
||||
|
||||
/**
|
||||
* Default singleton instance of {@linkplain ClassHierarchyResolver}
|
||||
* using {@link ClassLoader#getSystemResourceAsStream(String)}
|
||||
* as the {@code ClassStreamResolver}
|
||||
* Returns a default instance of {@linkplain ClassHierarchyResolver}
|
||||
* that reads from system class loader with
|
||||
* {@link ClassLoader#getSystemResourceAsStream(String)} and falls
|
||||
* back to reflection if a class is not found.
|
||||
*/
|
||||
ClassHierarchyResolver DEFAULT_CLASS_HIERARCHY_RESOLVER
|
||||
= new ClassHierarchyImpl.CachedClassHierarchyResolver(
|
||||
new Function<ClassDesc, InputStream>() {
|
||||
@Override
|
||||
public InputStream apply(ClassDesc classDesc) {
|
||||
return ClassLoader.getSystemResourceAsStream(Util.toInternalName(classDesc) + ".class");
|
||||
}
|
||||
});
|
||||
static ClassHierarchyResolver defaultResolver() {
|
||||
return ClassHierarchyImpl.DEFAULT_RESOLVER;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the {@link ClassHierarchyInfo} for a given class name, or null
|
||||
|
@ -61,6 +64,31 @@ public interface ClassHierarchyResolver {
|
|||
*/
|
||||
ClassHierarchyInfo getClassInfo(ClassDesc classDesc);
|
||||
|
||||
/**
|
||||
* Information about a resolved class.
|
||||
*/
|
||||
sealed interface ClassHierarchyInfo permits ClassHierarchyImpl.ClassHierarchyInfoImpl {
|
||||
|
||||
/**
|
||||
* Indicates that a class is a declared class, with the specific given super class.
|
||||
*
|
||||
* @param superClass descriptor of the super class, may be {@code null}
|
||||
* @return the info indicating the super class
|
||||
*/
|
||||
static ClassHierarchyInfo ofClass(ClassDesc superClass) {
|
||||
return new ClassHierarchyImpl.ClassHierarchyInfoImpl(superClass, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that a class is an interface.
|
||||
*
|
||||
* @return the info indicating an interface
|
||||
*/
|
||||
static ClassHierarchyInfo ofInterface() {
|
||||
return new ClassHierarchyImpl.ClassHierarchyInfoImpl(CD_Object, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chains this {@linkplain ClassHierarchyResolver} with another to be
|
||||
* consulted if this resolver does not know about the specified class.
|
||||
|
@ -74,30 +102,73 @@ public interface ClassHierarchyResolver {
|
|||
public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) {
|
||||
var chi = ClassHierarchyResolver.this.getClassInfo(classDesc);
|
||||
if (chi == null)
|
||||
chi = other.getClassInfo(classDesc);
|
||||
return other.getClassInfo(classDesc);
|
||||
return chi;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about a resolved class.
|
||||
* @param thisClass descriptor of this class
|
||||
* @param isInterface whether this class is an interface
|
||||
* @param superClass descriptor of the superclass (not relevant for interfaces)
|
||||
* Returns a ClassHierarchyResolver that caches class hierarchy information from this
|
||||
* resolver. The returned resolver will not update if delegate resolver returns differently.
|
||||
* The thread safety of the returned resolver depends on the thread safety of the map
|
||||
* returned by the {@code cacheFactory}.
|
||||
*
|
||||
* @param cacheFactory the factory for the cache
|
||||
* @return the ClassHierarchyResolver with caching
|
||||
*/
|
||||
public record ClassHierarchyInfo(ClassDesc thisClass, boolean isInterface, ClassDesc superClass) {
|
||||
default ClassHierarchyResolver cached(Supplier<Map<ClassDesc, ClassHierarchyInfo>> cacheFactory) {
|
||||
return new ClassHierarchyImpl.CachedClassHierarchyResolver(this, cacheFactory.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy
|
||||
* information from classfiles located by a mapping function
|
||||
* Returns a ClassHierarchyResolver that caches class hierarchy information from this
|
||||
* resolver. The returned resolver will not update if delegate resolver returns differently.
|
||||
* The returned resolver is not thread-safe.
|
||||
* {@snippet file="PackageSnippets.java" region="lookup-class-hierarchy-resolver"}
|
||||
*
|
||||
* @return the ClassHierarchyResolver
|
||||
*/
|
||||
default ClassHierarchyResolver cached() {
|
||||
record Factory() implements Supplier<Map<ClassDesc, ClassHierarchyInfo>> {
|
||||
// uses a record as we cannot declare a local class static
|
||||
static final Factory INSTANCE = new Factory();
|
||||
|
||||
@Override
|
||||
public Map<ClassDesc, ClassHierarchyInfo> get() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
return cached(Factory.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy
|
||||
* information from classfiles located by a mapping function. The mapping function
|
||||
* should return null if it cannot provide a mapping for a classfile. Any IOException
|
||||
* from the provided input stream is rethrown as an UncheckedIOException.
|
||||
*
|
||||
* @param classStreamResolver maps class descriptors to classfile input streams
|
||||
* @return the {@linkplain ClassHierarchyResolver}
|
||||
*/
|
||||
public static ClassHierarchyResolver ofCached(Function<ClassDesc, InputStream> classStreamResolver) {
|
||||
return new ClassHierarchyImpl.CachedClassHierarchyResolver(classStreamResolver);
|
||||
static ClassHierarchyResolver ofResourceParsing(Function<ClassDesc, InputStream> classStreamResolver) {
|
||||
return new ClassHierarchyImpl.ResourceParsingClassHierarchyResolver(classStreamResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy
|
||||
* information from classfiles located by a class loader.
|
||||
*
|
||||
* @param loader the class loader, to find class files
|
||||
* @return the {@linkplain ClassHierarchyResolver}
|
||||
*/
|
||||
static ClassHierarchyResolver ofResourceParsing(ClassLoader loader) {
|
||||
return ofResourceParsing(new Function<>() {
|
||||
@Override
|
||||
public InputStream apply(ClassDesc classDesc) {
|
||||
return loader.getResourceAsStream(Util.toInternalName(classDesc) + ".class");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,8 +179,52 @@ public interface ClassHierarchyResolver {
|
|||
* @param classToSuperClass a map from classes to their super classes
|
||||
* @return the {@linkplain ClassHierarchyResolver}
|
||||
*/
|
||||
public static ClassHierarchyResolver of(Collection<ClassDesc> interfaces,
|
||||
static ClassHierarchyResolver of(Collection<ClassDesc> interfaces,
|
||||
Map<ClassDesc, ClassDesc> classToSuperClass) {
|
||||
return new ClassHierarchyImpl.StaticClassHierarchyResolver(interfaces, classToSuperClass);
|
||||
return new StaticClassHierarchyResolver(interfaces, classToSuperClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassHierarchyResolver that extracts class hierarchy information via
|
||||
* the Reflection API with a {@linkplain ClassLoader}.
|
||||
*
|
||||
* @param loader the class loader
|
||||
* @return the class hierarchy resolver
|
||||
*/
|
||||
static ClassHierarchyResolver ofClassLoading(ClassLoader loader) {
|
||||
return new ClassLoadingClassHierarchyResolver(new Function<>() {
|
||||
@Override
|
||||
public Class<?> apply(ClassDesc cd) {
|
||||
try {
|
||||
return Class.forName(Util.toBinaryName(cd.descriptorString()), false, loader);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassHierarchyResolver that extracts class hierarchy information via
|
||||
* the Reflection API with a {@linkplain MethodHandles.Lookup Lookup}. If the class
|
||||
* resolved is inaccessible to the given lookup, it throws {@link
|
||||
* IllegalArgumentException} instead of returning {@code null}.
|
||||
*
|
||||
* @param lookup the lookup, must be able to access classes to resolve
|
||||
* @return the class hierarchy resolver
|
||||
*/
|
||||
static ClassHierarchyResolver ofClassLoading(MethodHandles.Lookup lookup) {
|
||||
return new ClassLoadingClassHierarchyResolver(new Function<>() {
|
||||
@Override
|
||||
public Class<?> apply(ClassDesc cd) {
|
||||
try {
|
||||
return cd.resolveConstantDesc(lookup);
|
||||
} catch (IllegalAccessException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
|
@ -25,17 +25,22 @@ 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.lang.constant.ConstantDescs;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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.
|
||||
|
@ -43,6 +48,20 @@ import jdk.internal.classfile.ClassHierarchyResolver;
|
|||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -53,9 +72,9 @@ public final class ClassHierarchyImpl {
|
|||
this.resolver = classHierarchyResolver;
|
||||
}
|
||||
|
||||
private ClassHierarchyResolver.ClassHierarchyInfo resolve(ClassDesc classDesc) {
|
||||
private ClassHierarchyInfoImpl resolve(ClassDesc classDesc) {
|
||||
var res = resolver.getClassInfo(classDesc);
|
||||
if (res != null) return res;
|
||||
if (res != null) return (ClassHierarchyInfoImpl) res;
|
||||
throw new IllegalArgumentException("Could not resolve class " + classDesc.displayName());
|
||||
}
|
||||
|
||||
|
@ -79,7 +98,7 @@ public final class ClassHierarchyImpl {
|
|||
//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 ConstantDescs.CD_Object;
|
||||
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;
|
||||
|
@ -101,83 +120,94 @@ public final class ClassHierarchyImpl {
|
|||
}
|
||||
|
||||
public static final class CachedClassHierarchyResolver implements ClassHierarchyResolver {
|
||||
|
||||
//this instance should never appear in the cache nor leak out
|
||||
//this instance should leak out, appears only in cache in order to utilize Map.computeIfAbsent
|
||||
private static final ClassHierarchyResolver.ClassHierarchyInfo NOPE =
|
||||
new ClassHierarchyResolver.ClassHierarchyInfo(null, false, null);
|
||||
new ClassHierarchyInfoImpl(null, true);
|
||||
|
||||
private final Function<ClassDesc, InputStream> streamProvider;
|
||||
private final Map<ClassDesc, ClassHierarchyResolver.ClassHierarchyInfo> resolvedCache;
|
||||
private final Map<ClassDesc, ClassHierarchyInfo> resolvedCache;
|
||||
private final Function<ClassDesc, ClassHierarchyInfo> delegateFunction;
|
||||
|
||||
public CachedClassHierarchyResolver(Function<ClassDesc, InputStream> classStreamProvider) {
|
||||
this.streamProvider = classStreamProvider;
|
||||
this.resolvedCache = Collections.synchronizedMap(new HashMap<>());
|
||||
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) {
|
||||
//using NOPE to distinguish between null value and non-existent record in the cache
|
||||
//this code is on JDK bootstrap critical path, so cannot use lambdas here
|
||||
var res = resolvedCache.getOrDefault(classDesc, NOPE);
|
||||
if (res == NOPE) {
|
||||
res = null;
|
||||
var ci = streamProvider.apply(classDesc);
|
||||
if (ci != 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++) {
|
||||
switch (in.readUnsignedByte()) {
|
||||
case 1 -> cpStrings[i] = in.readUTF();
|
||||
case 7 -> cpClasses[i] = in.readUnsignedShort();
|
||||
case 8, 16, 19, 20 -> in.skipBytes(2);
|
||||
case 15 -> in.skipBytes(3);
|
||||
case 3, 4, 9, 10, 11, 12, 17, 18 -> in.skipBytes(4);
|
||||
case 5, 6 -> {in.skipBytes(8); i++;}
|
||||
}
|
||||
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++;
|
||||
}
|
||||
boolean isInterface = (in.readUnsignedShort() & 0x0200) != 0;
|
||||
in.skipBytes(2);
|
||||
int superIndex = in.readUnsignedShort();
|
||||
var superClass = superIndex > 0 ? ClassDesc.ofInternalName(cpStrings[cpClasses[superIndex]]) : null;
|
||||
res = new ClassHierarchyInfo(classDesc, isInterface, superClass);
|
||||
int interfCount = in.readUnsignedShort();
|
||||
for (int i=0; i<interfCount; i++) {
|
||||
//all listed interfaces are cached without resolution
|
||||
var intDesc = ClassDesc.ofInternalName(cpStrings[cpClasses[in.readUnsignedShort()]]);
|
||||
resolvedCache.put(intDesc, new ClassHierarchyResolver.ClassHierarchyInfo(intDesc, true, null));
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
//ignore
|
||||
default -> throw new IllegalStateException("Bad tag (" + tag + ") at index (" + i + ")");
|
||||
}
|
||||
}
|
||||
//null ClassHierarchyInfo value is also cached to avoid repeated resolution attempts
|
||||
resolvedCache.put(classDesc, res);
|
||||
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);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class StaticClassHierarchyResolver implements ClassHierarchyResolver {
|
||||
|
||||
private static final ClassHierarchyInfo CHI_Object =
|
||||
new ClassHierarchyInfo(ConstantDescs.CD_Object, false, null);
|
||||
|
||||
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(ConstantDescs.CD_Object, CHI_Object);
|
||||
map.put(CD_Object, ClassHierarchyInfoImpl.OBJECT_INFO);
|
||||
for (var e : classToSuperClass.entrySet())
|
||||
map.put(e.getKey(), new ClassHierarchyInfo(e.getKey(), false, e.getValue()));
|
||||
map.put(e.getKey(), ClassHierarchyInfo.ofClass(e.getValue()));
|
||||
for (var i : interfaceNames)
|
||||
map.put(i, new ClassHierarchyInfo(i, true, null));
|
||||
map.put(i, ClassHierarchyInfo.ofInterface());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -185,4 +215,39 @@ public final class ClassHierarchyImpl {
|
|||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import jdk.internal.classfile.ClassHierarchyResolver;
|
|||
import jdk.internal.classfile.Classfile;
|
||||
import jdk.internal.classfile.constantpool.Utf8Entry;
|
||||
|
||||
import static jdk.internal.classfile.ClassHierarchyResolver.DEFAULT_CLASS_HIERARCHY_RESOLVER;
|
||||
import static jdk.internal.classfile.ClassHierarchyResolver.defaultResolver;
|
||||
|
||||
public class Options {
|
||||
|
||||
|
@ -52,7 +52,7 @@ public class Options {
|
|||
public Boolean fixJumps = true;
|
||||
public Boolean patchCode = true;
|
||||
public Boolean filterDeadLabels = false;
|
||||
public ClassHierarchyResolver classHierarchyResolver = DEFAULT_CLASS_HIERARCHY_RESOLVER;
|
||||
public ClassHierarchyResolver classHierarchyResolver = defaultResolver();
|
||||
public Function<Utf8Entry, AttributeMapper<?>> attributeMapper = new Function<>() {
|
||||
@Override
|
||||
public AttributeMapper<?> apply(Utf8Entry k) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* 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
|
||||
|
@ -84,9 +84,26 @@ public class Util {
|
|||
return count;
|
||||
}
|
||||
|
||||
public static String toClassString(String desc) {
|
||||
//TODO: this doesn't look right L ... ;
|
||||
return desc.replace('/', '.');
|
||||
/**
|
||||
* Convert a descriptor of classes or interfaces or arrays, or an internal
|
||||
* name of a class or interface, into a fully qualified binary name, that can
|
||||
* be resolved by {@link Class#forName(String) Class::forName}. Primitive type
|
||||
* descriptors should never be passed into this method.
|
||||
*
|
||||
* @param descOrInternalName a descriptor or internal name
|
||||
* @return the fully qualified binary name
|
||||
*/
|
||||
public static String toBinaryName(String descOrInternalName) {
|
||||
if (descOrInternalName.startsWith("L")) {
|
||||
// descriptors of classes or interfaces
|
||||
if (descOrInternalName.length() <= 2 || !descOrInternalName.endsWith(";")) {
|
||||
throw new IllegalArgumentException(descOrInternalName);
|
||||
}
|
||||
return descOrInternalName.substring(1, descOrInternalName.length() - 1).replace('/', '.');
|
||||
} else {
|
||||
// arrays, classes or interfaces' internal names
|
||||
return descOrInternalName.replace('/', '.');
|
||||
}
|
||||
}
|
||||
|
||||
public static Iterator<String> parameterTypes(String s) {
|
||||
|
|
|
@ -105,7 +105,7 @@ public final class VerifierImpl {
|
|||
static final int MAX_CODE_SIZE = 65535;
|
||||
|
||||
public static List<VerifyError> verify(ClassModel classModel, Consumer<String> logger) {
|
||||
return verify(classModel, ClassHierarchyResolver.DEFAULT_CLASS_HIERARCHY_RESOLVER, logger);
|
||||
return verify(classModel, ClassHierarchyResolver.defaultResolver(), logger);
|
||||
}
|
||||
|
||||
public static List<VerifyError> verify(ClassModel classModel, ClassHierarchyResolver classHierarchyResolver, Consumer<String> logger) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.constant.ConstantDescs;
|
||||
import java.lang.constant.MethodTypeDesc;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -33,7 +34,10 @@ import java.util.LinkedList;
|
|||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.classfile.ClassElement;
|
||||
import jdk.internal.classfile.ClassHierarchyResolver;
|
||||
import jdk.internal.classfile.ClassModel;
|
||||
import jdk.internal.classfile.ClassTransform;
|
||||
import jdk.internal.classfile.Classfile;
|
||||
|
@ -107,7 +111,7 @@ class PackageSnippets {
|
|||
ClassModel cm = Classfile.parse(bytes);
|
||||
Set<ClassDesc> dependencies =
|
||||
cm.elementStream()
|
||||
.flatMap(ce -> ce instanceof MethodMethod mm ? mm.elementStream() : Stream.empty())
|
||||
.flatMap(ce -> ce instanceof MethodModel mm ? mm.elementStream() : Stream.empty())
|
||||
.flatMap(me -> me instanceof CodeModel com ? com.elementStream() : Stream.empty())
|
||||
.<ClassDesc>mapMulti((xe, c) -> {
|
||||
switch (xe) {
|
||||
|
@ -303,4 +307,11 @@ class PackageSnippets {
|
|||
.andThen(instrumentorClassRemapper)))));
|
||||
}
|
||||
// @end
|
||||
|
||||
void resolverExample() {
|
||||
// @start region="lookup-class-hierarchy-resolver"
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup(); // @replace regex="MethodHandles\.lookup\(\)" replacement="..."
|
||||
ClassHierarchyResolver resolver = ClassHierarchyResolver.ofClassLoading(lookup).cached();
|
||||
// @end
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue