Skip to content

Commit ac3ce2b

Browse files
liachasotona
authored andcommitted
8304425: ClassHierarchyResolver from Reflection
Reviewed-by: asotona
1 parent 79a4ac7 commit ac3ce2b

File tree

10 files changed

+372
-97
lines changed

10 files changed

+372
-97
lines changed

src/java.base/share/classes/jdk/internal/classfile/ClassHierarchyResolver.java

Lines changed: 140 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,12 +26,19 @@
2626

2727
import java.io.InputStream;
2828
import java.lang.constant.ClassDesc;
29+
import java.lang.invoke.MethodHandles;
2930
import java.util.Collection;
31+
import java.util.HashMap;
3032
import java.util.Map;
3133
import java.util.function.Function;
32-
import jdk.internal.classfile.impl.Util;
34+
import java.util.function.Supplier;
3335

3436
import jdk.internal.classfile.impl.ClassHierarchyImpl;
37+
import jdk.internal.classfile.impl.ClassHierarchyImpl.ClassLoadingClassHierarchyResolver;
38+
import jdk.internal.classfile.impl.ClassHierarchyImpl.StaticClassHierarchyResolver;
39+
import jdk.internal.classfile.impl.Util;
40+
41+
import static java.lang.constant.ConstantDescs.CD_Object;
3542

3643
/**
3744
* Provides class hierarchy information for generating correct stack maps
@@ -41,18 +48,14 @@
4148
public interface ClassHierarchyResolver {
4249

4350
/**
44-
* Default singleton instance of {@linkplain ClassHierarchyResolver}
45-
* using {@link ClassLoader#getSystemResourceAsStream(String)}
46-
* as the {@code ClassStreamResolver}
51+
* Returns a default instance of {@linkplain ClassHierarchyResolver}
52+
* that reads from system class loader with
53+
* {@link ClassLoader#getSystemResourceAsStream(String)} and falls
54+
* back to reflection if a class is not found.
4755
*/
48-
ClassHierarchyResolver DEFAULT_CLASS_HIERARCHY_RESOLVER
49-
= new ClassHierarchyImpl.CachedClassHierarchyResolver(
50-
new Function<ClassDesc, InputStream>() {
51-
@Override
52-
public InputStream apply(ClassDesc classDesc) {
53-
return ClassLoader.getSystemResourceAsStream(Util.toInternalName(classDesc) + ".class");
54-
}
55-
});
56+
static ClassHierarchyResolver defaultResolver() {
57+
return ClassHierarchyImpl.DEFAULT_RESOLVER;
58+
}
5659

5760
/**
5861
* {@return the {@link ClassHierarchyInfo} for a given class name, or null
@@ -61,6 +64,31 @@ public InputStream apply(ClassDesc classDesc) {
6164
*/
6265
ClassHierarchyInfo getClassInfo(ClassDesc classDesc);
6366

67+
/**
68+
* Information about a resolved class.
69+
*/
70+
sealed interface ClassHierarchyInfo permits ClassHierarchyImpl.ClassHierarchyInfoImpl {
71+
72+
/**
73+
* Indicates that a class is a declared class, with the specific given super class.
74+
*
75+
* @param superClass descriptor of the super class, may be {@code null}
76+
* @return the info indicating the super class
77+
*/
78+
static ClassHierarchyInfo ofClass(ClassDesc superClass) {
79+
return new ClassHierarchyImpl.ClassHierarchyInfoImpl(superClass, false);
80+
}
81+
82+
/**
83+
* Indicates that a class is an interface.
84+
*
85+
* @return the info indicating an interface
86+
*/
87+
static ClassHierarchyInfo ofInterface() {
88+
return new ClassHierarchyImpl.ClassHierarchyInfoImpl(CD_Object, true);
89+
}
90+
}
91+
6492
/**
6593
* Chains this {@linkplain ClassHierarchyResolver} with another to be
6694
* consulted if this resolver does not know about the specified class.
@@ -74,30 +102,73 @@ default ClassHierarchyResolver orElse(ClassHierarchyResolver other) {
74102
public ClassHierarchyInfo getClassInfo(ClassDesc classDesc) {
75103
var chi = ClassHierarchyResolver.this.getClassInfo(classDesc);
76104
if (chi == null)
77-
chi = other.getClassInfo(classDesc);
105+
return other.getClassInfo(classDesc);
78106
return chi;
79107
}
80108
};
81109
}
82110

83111
/**
84-
* Information about a resolved class.
85-
* @param thisClass descriptor of this class
86-
* @param isInterface whether this class is an interface
87-
* @param superClass descriptor of the superclass (not relevant for interfaces)
112+
* Returns a ClassHierarchyResolver that caches class hierarchy information from this
113+
* resolver. The returned resolver will not update if delegate resolver returns differently.
114+
* The thread safety of the returned resolver depends on the thread safety of the map
115+
* returned by the {@code cacheFactory}.
116+
*
117+
* @param cacheFactory the factory for the cache
118+
* @return the ClassHierarchyResolver with caching
88119
*/
89-
public record ClassHierarchyInfo(ClassDesc thisClass, boolean isInterface, ClassDesc superClass) {
120+
default ClassHierarchyResolver cached(Supplier<Map<ClassDesc, ClassHierarchyInfo>> cacheFactory) {
121+
return new ClassHierarchyImpl.CachedClassHierarchyResolver(this, cacheFactory.get());
90122
}
91123

92124
/**
93-
* Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy
94-
* information from classfiles located by a mapping function
125+
* Returns a ClassHierarchyResolver that caches class hierarchy information from this
126+
* resolver. The returned resolver will not update if delegate resolver returns differently.
127+
* The returned resolver is not thread-safe.
128+
* {@snippet file="PackageSnippets.java" region="lookup-class-hierarchy-resolver"}
129+
*
130+
* @return the ClassHierarchyResolver
131+
*/
132+
default ClassHierarchyResolver cached() {
133+
record Factory() implements Supplier<Map<ClassDesc, ClassHierarchyInfo>> {
134+
// uses a record as we cannot declare a local class static
135+
static final Factory INSTANCE = new Factory();
136+
137+
@Override
138+
public Map<ClassDesc, ClassHierarchyInfo> get() {
139+
return new HashMap<>();
140+
}
141+
}
142+
return cached(Factory.INSTANCE);
143+
}
144+
145+
/**
146+
* Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy
147+
* information from classfiles located by a mapping function. The mapping function
148+
* should return null if it cannot provide a mapping for a classfile. Any IOException
149+
* from the provided input stream is rethrown as an UncheckedIOException.
95150
*
96151
* @param classStreamResolver maps class descriptors to classfile input streams
97152
* @return the {@linkplain ClassHierarchyResolver}
98153
*/
99-
public static ClassHierarchyResolver ofCached(Function<ClassDesc, InputStream> classStreamResolver) {
100-
return new ClassHierarchyImpl.CachedClassHierarchyResolver(classStreamResolver);
154+
static ClassHierarchyResolver ofResourceParsing(Function<ClassDesc, InputStream> classStreamResolver) {
155+
return new ClassHierarchyImpl.ResourceParsingClassHierarchyResolver(classStreamResolver);
156+
}
157+
158+
/**
159+
* Returns a {@linkplain ClassHierarchyResolver} that extracts class hierarchy
160+
* information from classfiles located by a class loader.
161+
*
162+
* @param loader the class loader, to find class files
163+
* @return the {@linkplain ClassHierarchyResolver}
164+
*/
165+
static ClassHierarchyResolver ofResourceParsing(ClassLoader loader) {
166+
return ofResourceParsing(new Function<>() {
167+
@Override
168+
public InputStream apply(ClassDesc classDesc) {
169+
return loader.getResourceAsStream(Util.toInternalName(classDesc) + ".class");
170+
}
171+
});
101172
}
102173

103174
/**
@@ -108,8 +179,52 @@ public static ClassHierarchyResolver ofCached(Function<ClassDesc, InputStream> c
108179
* @param classToSuperClass a map from classes to their super classes
109180
* @return the {@linkplain ClassHierarchyResolver}
110181
*/
111-
public static ClassHierarchyResolver of(Collection<ClassDesc> interfaces,
182+
static ClassHierarchyResolver of(Collection<ClassDesc> interfaces,
112183
Map<ClassDesc, ClassDesc> classToSuperClass) {
113-
return new ClassHierarchyImpl.StaticClassHierarchyResolver(interfaces, classToSuperClass);
184+
return new StaticClassHierarchyResolver(interfaces, classToSuperClass);
185+
}
186+
187+
/**
188+
* Returns a ClassHierarchyResolver that extracts class hierarchy information via
189+
* the Reflection API with a {@linkplain ClassLoader}.
190+
*
191+
* @param loader the class loader
192+
* @return the class hierarchy resolver
193+
*/
194+
static ClassHierarchyResolver ofClassLoading(ClassLoader loader) {
195+
return new ClassLoadingClassHierarchyResolver(new Function<>() {
196+
@Override
197+
public Class<?> apply(ClassDesc cd) {
198+
try {
199+
return Class.forName(Util.toBinaryName(cd.descriptorString()), false, loader);
200+
} catch (ClassNotFoundException ex) {
201+
return null;
202+
}
203+
}
204+
});
205+
}
206+
207+
/**
208+
* Returns a ClassHierarchyResolver that extracts class hierarchy information via
209+
* the Reflection API with a {@linkplain MethodHandles.Lookup Lookup}. If the class
210+
* resolved is inaccessible to the given lookup, it throws {@link
211+
* IllegalArgumentException} instead of returning {@code null}.
212+
*
213+
* @param lookup the lookup, must be able to access classes to resolve
214+
* @return the class hierarchy resolver
215+
*/
216+
static ClassHierarchyResolver ofClassLoading(MethodHandles.Lookup lookup) {
217+
return new ClassLoadingClassHierarchyResolver(new Function<>() {
218+
@Override
219+
public Class<?> apply(ClassDesc cd) {
220+
try {
221+
return cd.resolveConstantDesc(lookup);
222+
} catch (IllegalAccessException ex) {
223+
throw new IllegalArgumentException(ex);
224+
} catch (ReflectiveOperationException ex) {
225+
return null;
226+
}
227+
}
228+
});
114229
}
115230
}

0 commit comments

Comments
 (0)