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
2626
2727import java .io .InputStream ;
2828import java .lang .constant .ClassDesc ;
29+ import java .lang .invoke .MethodHandles ;
2930import java .util .Collection ;
31+ import java .util .HashMap ;
3032import java .util .Map ;
3133import java .util .function .Function ;
32- import jdk . internal . classfile . impl . Util ;
34+ import java . util . function . Supplier ;
3335
3436import 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
4148public 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