• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.19
/byte-buddy-dep/src/main/java/net/bytebuddy/TypeCache.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.build.CachedReturnPlugin;
20
import net.bytebuddy.utility.CompoundList;
21
import net.bytebuddy.utility.nullability.AlwaysNull;
22
import net.bytebuddy.utility.nullability.MaybeNull;
23

24
import java.lang.ref.Reference;
25
import java.lang.ref.ReferenceQueue;
26
import java.lang.ref.SoftReference;
27
import java.lang.ref.WeakReference;
28
import java.util.ArrayList;
29
import java.util.Arrays;
30
import java.util.Collection;
31
import java.util.HashSet;
32
import java.util.Set;
33
import java.util.concurrent.Callable;
34
import java.util.concurrent.ConcurrentHashMap;
35
import java.util.concurrent.ConcurrentMap;
36

37
/**
38
 * <p>
39
 * A cache for storing types without strongly referencing any class loader or type.
40
 * </p>
41
 * <p>
42
 * <b>Note</b>: In order to clean obsolete class loader references from the map, {@link TypeCache#expungeStaleEntries()} must be called
43
 * regularly. This can happen in a different thread, in custom intervals or on every use of the cache by creating an instance of
44
 * {@link WithInlineExpunction}. This cache is fully thread-safe.
45
 * </p>
46
 * <p>
47
 * <b>Important</b>: The behavior of a type cache might not be as expected. A class is only eligible for garbage collection once its class
48
 * loader is eligible for garbage collection. At the same time, a garbage collector is only eligible for garbage collection once all of
49
 * its classes are eligible for garbage collection. If this cache referenced the cached type strongly, this would never be the case which
50
 * is why this cache maintains either strong or weak references. In the latter case, a type is typically retained until the last instance of
51
 * the type is not eligible for garbage collection. With soft references, the type is typically retained until the next full garbage collection
52
 * where all instances of the type are eligible for garbage collection.
53
 * </p>
54
 *
55
 * @param <T> The type of the key that is used for identifying stored classes per class loader. Such keys must not strongly reference any
56
 *            types or class loaders without potentially corrupting the garbage eligibility of stored classes. As the storage is segmented
57
 *            by class loader, it is normally sufficient to store types by their name.
58
 * @see WithInlineExpunction
59
 * @see SimpleKey
60
 */
61
public class TypeCache<T> extends ReferenceQueue<ClassLoader> {
62

63
    /**
64
     * Indicates that a type was not found.
65
     */
66
    @AlwaysNull
67
    private static final Class<?> NOT_FOUND = null;
1✔
68

69
    /**
70
     * The reference type to use for stored types.
71
     */
72
    protected final Sort sort;
73

74
    /**
75
     * The underlying map containing cached objects.
76
     */
77
    protected final ConcurrentMap<StorageKey, ConcurrentMap<T, Object>> cache;
78

79
    /**
80
     * Creates a new type cache with strong references to the stored types.
81
     */
82
    public TypeCache() {
83
        this(Sort.STRONG);
1✔
84
    }
1✔
85

86
    /**
87
     * Creates a new type cache.
88
     *
89
     * @param sort The reference type to use for stored types.
90
     */
91
    public TypeCache(Sort sort) {
1✔
92
        this.sort = sort;
1✔
93
        cache = new ConcurrentHashMap<StorageKey, ConcurrentMap<T, Object>>();
1✔
94
    }
1✔
95

96
    /**
97
     * Finds a stored type or returns {@code null} if no type was stored.
98
     *
99
     * @param classLoader The class loader for which this type is stored or {@code null} for the bootstrap loader.
100
     * @param key         The key for the type in question.
101
     * @return The stored type or {@code null} if no type was stored.
102
     */
103
    @MaybeNull
104
    @SuppressFBWarnings(value = "GC_UNRELATED_TYPES", justification = "Cross-comparison is intended.")
105
    public Class<?> find(@MaybeNull ClassLoader classLoader, T key) {
106
        ConcurrentMap<T, Object> storage = cache.get(new LookupKey(classLoader));
1✔
107
        if (storage == null) {
1✔
108
            return NOT_FOUND;
1✔
109
        } else {
110
            Object value = storage.get(key);
1✔
111
            if (value == null) {
1✔
112
                return NOT_FOUND;
1✔
113
            } else if (value instanceof Reference<?>) {
1✔
114
                return (Class<?>) ((Reference<?>) value).get();
1✔
115
            } else {
116
                return (Class<?>) value;
1✔
117
            }
118
        }
119
    }
120

121
    /**
122
     * Inserts a new type into the cache. If a type with the same class loader and key was inserted previously, the cache is not updated.
123
     *
124
     * @param classLoader The class loader for which this type is stored or {@code null} for the bootstrap loader.
125
     * @param key         The key for the type in question.
126
     * @param type        The type to insert of no previous type was stored in the cache.
127
     * @return The supplied type or a previously submitted type for the same class loader and key combination.
128
     */
129
    @SuppressFBWarnings(value = "GC_UNRELATED_TYPES", justification = "Cross-comparison is intended.")
130
    public Class<?> insert(@MaybeNull ClassLoader classLoader, T key, Class<?> type) {
131
        ConcurrentMap<T, Object> storage = cache.get(new LookupKey(classLoader));
1✔
132
        if (storage == null) {
1✔
133
            storage = new ConcurrentHashMap<T, Object>();
1✔
134
            ConcurrentMap<T, Object> previous = cache.putIfAbsent(new StorageKey(classLoader, this), storage);
1✔
135
            if (previous != null) {
1✔
136
                storage = previous;
×
137
            }
138
        }
139
        Object value = sort.wrap(type), previous = storage.putIfAbsent(key, value);
1✔
140
        while (previous != null) {
1✔
141
            Class<?> previousType = (Class<?>) (previous instanceof Reference<?>
1✔
142
                    ? ((Reference<?>) previous).get()
1✔
143
                    : previous);
144
            if (previousType != null) {
1✔
145
                return previousType;
×
146
            } else if (storage.remove(key, previous)) {
1✔
147
                previous = storage.putIfAbsent(key, value);
1✔
148
            } else {
149
                previous = storage.get(key);
×
150
                if (previous == null) {
×
151
                    previous = storage.putIfAbsent(key, value);
×
152
                }
153
            }
154
        }
1✔
155
        return type;
1✔
156
    }
157

158
    /**
159
     * Finds an existing type or inserts a new one if the previous type was not found.
160
     *
161
     * @param classLoader The class loader for which this type is stored or {@code null} for the bootstrap loader.
162
     * @param key         The key for the type in question.
163
     * @param lazy        A lazy creator for the type to insert of no previous type was stored in the cache.
164
     * @return The lazily created type or a previously submitted type for the same class loader and key combination.
165
     */
166
    public Class<?> findOrInsert(@MaybeNull ClassLoader classLoader, T key, Callable<Class<?>> lazy) {
167
        Class<?> type = find(classLoader, key);
1✔
168
        if (type != null) {
1✔
169
            return type;
1✔
170
        } else {
171
            try {
172
                return insert(classLoader, key, lazy.call());
1✔
173
            } catch (Throwable throwable) {
1✔
174
                throw new IllegalArgumentException("Could not create type", throwable);
1✔
175
            }
176
        }
177
    }
178

179
    /**
180
     * Finds an existing type or inserts a new one if the previous type was not found.
181
     *
182
     * @param classLoader The class loader for which this type is stored or {@code null} for the bootstrap loader.
183
     * @param key         The key for the type in question.
184
     * @param lazy        A lazy creator for the type to insert of no previous type was stored in the cache.
185
     * @param monitor     A monitor to lock before creating the lazy type.
186
     * @return The lazily created type or a previously submitted type for the same class loader and key combination.
187
     */
188
    public Class<?> findOrInsert(@MaybeNull ClassLoader classLoader, T key, Callable<Class<?>> lazy, Object monitor) {
189
        Class<?> type = find(classLoader, key);
1✔
190
        if (type != null) {
1✔
191
            return type;
1✔
192
        } else {
193
            synchronized (monitor) {
1✔
194
                return findOrInsert(classLoader, key, lazy);
1✔
195
            }
196
        }
197
    }
198

199
    /**
200
     * Removes any stale class loader entries from the cache.
201
     */
202
    public void expungeStaleEntries() {
203
        Reference<?> reference;
204
        while ((reference = poll()) != null) {
1✔
205
            cache.remove(reference);
1✔
206
        }
207
    }
1✔
208

209
    /**
210
     * Clears the entire cache.
211
     */
212
    public void clear() {
213
        cache.clear();
1✔
214
    }
1✔
215

216
    /**
217
     * Determines the storage format for a cached type.
218
     */
219
    public enum Sort {
1✔
220

221
        /**
222
         * Creates a cache where cached types are wrapped by {@link WeakReference}s.
223
         */
224
        WEAK {
1✔
225
            @Override
226
            protected Reference<Class<?>> wrap(Class<?> type) {
227
                return new WeakReference<Class<?>>(type);
1✔
228
            }
229
        },
230

231
        /**
232
         * Creates a cache where cached types are wrapped by {@link SoftReference}s.
233
         */
234
        SOFT {
1✔
235
            @Override
236
            protected Reference<Class<?>> wrap(Class<?> type) {
237
                return new SoftReference<Class<?>>(type);
1✔
238
            }
239
        },
240

241
        /**
242
         * Creates a cache where cached types are strongly referenced.
243
         */
244
        STRONG {
1✔
245
            @Override
246
            protected Class<?> wrap(Class<?> type) {
247
                return type;
1✔
248
            }
249
        };
250

251
        /**
252
         * Wraps a type as a {@link Reference}.
253
         *
254
         * @param type The type to wrap.
255
         * @return The reference that represents the type.
256
         */
257
        protected abstract Object wrap(Class<?> type);
258
    }
259

260
    /**
261
     * A key used for looking up a previously inserted class loader cache.
262
     */
263
    protected static class LookupKey {
264

265
        /**
266
         * The referenced class loader or {@code null} for the bootstrap loader.
267
         */
268
        @MaybeNull
269
        private final ClassLoader classLoader;
270

271
        /**
272
         * The class loader's identity hash code.
273
         */
274
        private final int hashCode;
275

276
        /**
277
         * Creates a new lookup key.
278
         *
279
         * @param classLoader The represented class loader or {@code null} for the bootstrap loader.
280
         */
281
        protected LookupKey(@MaybeNull ClassLoader classLoader) {
1✔
282
            this.classLoader = classLoader;
1✔
283
            hashCode = System.identityHashCode(classLoader);
1✔
284
        }
1✔
285

286
        @Override
287
        public int hashCode() {
288
            return hashCode;
1✔
289
        }
290

291
        @Override
292
        @SuppressFBWarnings(value = "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", justification = "Cross-comparison is intended.")
293
        public boolean equals(@MaybeNull Object other) {
294
            if (this == other) {
1✔
295
                return true;
×
296
            } else if (other instanceof LookupKey) {
1✔
297
                return classLoader == ((LookupKey) other).classLoader;
×
298
            } else if (other instanceof StorageKey) {
1✔
299
                StorageKey storageKey = (StorageKey) other;
1✔
300
                return hashCode == storageKey.hashCode && classLoader == storageKey.get();
1✔
301
            } else {
302
                return false;
×
303
            }
304
        }
305
    }
306

307
    /**
308
     * A key used for storing a class loader cache reference.
309
     */
310
    protected static class StorageKey extends WeakReference<ClassLoader> {
311

312
        /**
313
         * The class loader's identity hash code.
314
         */
315
        private final int hashCode;
316

317
        /**
318
         * Creates a new storage key.
319
         *
320
         * @param classLoader    The represented class loader or {@code null} for the bootstrap loader.
321
         * @param referenceQueue The reference queue to notify upon a garbage collection.
322
         */
323
        protected StorageKey(@MaybeNull ClassLoader classLoader, ReferenceQueue<? super ClassLoader> referenceQueue) {
324
            super(classLoader, referenceQueue);
1✔
325
            hashCode = System.identityHashCode(classLoader);
1✔
326
        }
1✔
327

328
        @Override
329
        public int hashCode() {
330
            return hashCode;
1✔
331
        }
332

333
        @Override
334
        @SuppressFBWarnings(value = "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", justification = "Cross-comparison is intended.")
335
        public boolean equals(@MaybeNull Object other) {
336
            if (this == other) {
×
337
                return true;
×
338
            } else if (other instanceof LookupKey) {
×
339
                LookupKey lookupKey = (LookupKey) other;
×
340
                return hashCode == lookupKey.hashCode && get() == lookupKey.classLoader;
×
341
            } else if (other instanceof StorageKey) {
×
342
                StorageKey storageKey = (StorageKey) other;
×
343
                return hashCode == storageKey.hashCode && get() == storageKey.get();
×
344
            } else {
345
                return false;
×
346
            }
347
        }
348
    }
349

350
    /**
351
     * An implementation of a {@link TypeCache} where obsolete references are cleared upon any call.
352
     *
353
     * @param <S> The type of the key that is used for identifying stored classes per class loader. Such keys must not strongly reference any
354
     *            types or class loaders without potentially corrupting the garbage eligibility of stored classes. As the storage is segmented
355
     *            by class loader, it is normally sufficient to store types by their name.
356
     * @see TypeCache
357
     */
358
    public static class WithInlineExpunction<S> extends TypeCache<S> {
359

360
        /**
361
         * Creates a new type cache with inlined expunction and strong references to the stored types.
362
         */
363
        public WithInlineExpunction() {
364
            this(Sort.STRONG);
1✔
365
        }
1✔
366

367
        /**
368
         * Creates a new type cache with inlined expunction.
369
         *
370
         * @param sort The reference type to use for stored types.
371
         */
372
        public WithInlineExpunction(Sort sort) {
373
            super(sort);
1✔
374
        }
1✔
375

376
        /**
377
         * {@inheritDoc}
378
         */
379
        public Class<?> find(@MaybeNull ClassLoader classLoader, S key) {
380
            try {
381
                return super.find(classLoader, key);
1✔
382
            } finally {
383
                expungeStaleEntries();
1✔
384
            }
385
        }
386

387
        /**
388
         * {@inheritDoc}
389
         */
390
        public Class<?> insert(@MaybeNull ClassLoader classLoader, S key, Class<?> type) {
391
            try {
392
                return super.insert(classLoader, key, type);
1✔
393
            } finally {
394
                expungeStaleEntries();
1✔
395
            }
396
        }
397

398
        /**
399
         * {@inheritDoc}
400
         */
401
        public Class<?> findOrInsert(@MaybeNull ClassLoader classLoader, S key, Callable<Class<?>> builder) {
402
            try {
403
                return super.findOrInsert(classLoader, key, builder);
1✔
404
            } finally {
405
                expungeStaleEntries();
1✔
406
            }
407
        }
408

409
        /**
410
         * {@inheritDoc}
411
         */
412
        public Class<?> findOrInsert(@MaybeNull ClassLoader classLoader, S key, Callable<Class<?>> builder, Object monitor) {
413
            try {
414
                return super.findOrInsert(classLoader, key, builder, monitor);
1✔
415
            } finally {
416
                expungeStaleEntries();
1✔
417
            }
418
        }
419
    }
420

421
    /**
422
     * A simple key based on a collection of types where no type is strongly referenced.
423
     */
424
    public static class SimpleKey {
425

426
        /**
427
         * The referenced types.
428
         */
429
        private final Set<String> types;
430

431
        /**
432
         * Creates a simple cache key..
433
         *
434
         * @param type           The first type to be represented by this key.
435
         * @param additionalType Any additional types to be represented by this key.
436
         */
437
        public SimpleKey(Class<?> type, Class<?>... additionalType) {
438
            this(type, Arrays.asList(additionalType));
1✔
439
        }
1✔
440

441
        /**
442
         * Creates a simple cache key..
443
         *
444
         * @param type            The first type to be represented by this key.
445
         * @param additionalTypes Any additional types to be represented by this key.
446
         */
447
        public SimpleKey(Class<?> type, Collection<? extends Class<?>> additionalTypes) {
448
            this(CompoundList.of(type, new ArrayList<Class<?>>(additionalTypes)));
1✔
449
        }
1✔
450

451
        /**
452
         * Creates a simple cache key..
453
         *
454
         * @param types Any types to be represented by this key.
455
         */
456
        public SimpleKey(Collection<? extends Class<?>> types) {
1✔
457
            this.types = new HashSet<String>();
1✔
458
            for (Class<?> type : types) {
1✔
459
                this.types.add(type.getName());
1✔
460
            }
1✔
461
        }
1✔
462

463
        @Override
464
        @CachedReturnPlugin.Enhance("hashCode")
465
        public int hashCode() {
466
            return types.hashCode();
1✔
467
        }
468

469
        @Override
470
        public boolean equals(@MaybeNull Object other) {
471
            if (this == other) {
1✔
472
                return true;
×
473
            } else if (other == null || getClass() != other.getClass()) {
1✔
474
                return false;
×
475
            }
476
            SimpleKey simpleKey = (SimpleKey) other;
1✔
477
            return types.equals(simpleKey.types);
1✔
478
        }
479
    }
480
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc