Path: blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/BiClassValue.java
41154 views
/*1* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.dynalink;2627import java.lang.invoke.MethodHandles;28import java.lang.invoke.VarHandle;29import java.security.AccessControlContext;30import java.security.AccessController;31import java.security.PrivilegedAction;32import java.util.Map;33import java.util.Objects;34import java.util.function.BiFunction;35import java.util.function.Function;36import jdk.dynalink.internal.AccessControlContextFactory;3738import static jdk.dynalink.internal.InternalTypeUtilities.canReferenceDirectly;3940/**41* Similar to ClassValue, but lazily associates a computed value with42* (potentially) every pair of types.43* @param <T> the value to associate with pairs of types.44*/45final class BiClassValue<T> {46/**47* Creates a new BiClassValue that uses the specified binary function to48* lazily compute the values.49* @param compute the binary function to compute the values. Ordinarily, it50* is invoked at most once for any pair of values. However,51* it is possible for it to be invoked concurrently. It can52* even be invoked concurrently multiple times for the same53* arguments under contention. In that case, it is undefined54* which of the computed values will be retained therefore55* returning semantically equivalent values is strongly56* recommended; the function should ideally be pure.57* Additionally, if the pair of types passed as parameters58* are from unrelated class loaders, the computed value is59* not cached at all and the function might be reinvoked60* with the same parameters in the future. Finally, a null61* return value is allowed, but not cached.62* @param <T> the type of the values63* @return a new BiClassValue that computes the values using the passed64* function.65*/66static <T> BiClassValue<T> computing(final BiFunction<Class<?>, Class<?>, T> compute) {67return new BiClassValue<>(compute);68}6970/**71* A type-specific map that stores the values specific to pairs of types72* which include its class in one of the positions of the pair. Internally,73* it uses at most two maps named "forward" and "reverse". A BiClassValues74* for class C1 can store values for (C1, Cy) in its forward map as well75* as values for (Cx, C1) in its reverse map. The reason for this scheme76* is to avoid creating unwanted strong references from a parent class77* loader to a child class loader. If for a pair of classes (C1, C2)78* either C1 and C2 are in the same class loader, or C2 is in parent of C1,79* or C2 is a system class, forward map of C1's BiClassValues is used for80* storing the computed value. If C1 is in parent of C2, or C1 is a system81* class, reverse map of C2's BiClassValues is used for storing. If the82* class loaders are unrelated, the computed value is not cached and will83* be recomputed on every evaluation.84* NOTE that while every instance of this class is type-specific, it does85* not store a reference to the type Class object itself. BiClassValuesRoot86* creates the association from a type Class object to its BiClassValues'.87* @param <T> the type of the values88*/89private final static class BiClassValues<T> {90// These will be used for compareAndExchange on forward and reverse fields.91private static final VarHandle FORWARD;92private static final VarHandle REVERSE;93static {94final MethodHandles.Lookup lookup = MethodHandles.lookup();95try {96FORWARD = lookup.findVarHandle(BiClassValues.class, "forward", Map.class);97REVERSE = lookup.findVarHandle(BiClassValues.class, "reverse", Map.class);98} catch (NoSuchFieldException | IllegalAccessException e) {99throw new AssertionError(e);100}101}102103private Map<Class<?>, T> forward = Map.of();104private Map<Class<?>, T> reverse = Map.of();105106T getForwardValue(final Class<?> c) {107return forward.get(c);108}109110T getReverseValue(final Class<?> c) {111return reverse.get(c);112}113114private T compute(final VarHandle mapHandle, final Class<?> c, final Function<Class<?>, T> compute) {115@SuppressWarnings("unchecked")116Map<Class<?>, T> map = (Map<Class<?>, T>) mapHandle.getVolatile(this);117T value;118T newValue = null;119while ((value = map.get(c)) == null) {120if (newValue == null) {121newValue = compute.apply(c);122if (newValue == null) {123break;124}125}126@SuppressWarnings({"unchecked", "rawtypes"})127final Map.Entry<Class<?>, T>[] entries = map.entrySet().toArray(new Map.Entry[map.size() + 1]);128entries[map.size()] = Map.entry(c, newValue);129final var newMap = Map.ofEntries(entries);130@SuppressWarnings("unchecked")131final var witness = (Map<Class<?>, T>) mapHandle.compareAndExchange(this, map, newMap);132if (witness == map) {133value = newValue;134break;135}136map = witness;137}138return value;139}140141T computeForward(final Class<?> c, Function<Class<?>, T> compute) {142return compute(FORWARD, c, compute);143}144145T computeReverse(final Class<?> c, Function<Class<?>, T> compute) {146return compute(REVERSE, c, compute);147}148}149150// A named class used for "root" field so it can be static so it doesn't151// gain a synthetic this$0 reference as that'd cause a memory leak through152// unwanted anchoring to a GC root when used with system classes.153private static final class BiClassValuesRoot<T> extends ClassValue<BiClassValues<T>> {154@Override protected BiClassValues<T> computeValue(Class<?> type) {155return new BiClassValues<>();156}157}158159private enum RetentionDirection {160FORWARD,161REVERSE,162NEITHER163}164165private final BiClassValuesRoot<T> root = new BiClassValuesRoot<>();166private final BiFunction<Class<?>, Class<?>, T> compute;167168private BiClassValue(final BiFunction<Class<?>, Class<?>, T> compute) {169this.compute = Objects.requireNonNull(compute);170}171172final T get(final Class<?> c1, final Class<?> c2) {173// Most likely case: it is in the forward map of c1's BiClassValues174final BiClassValues<T> cv1 = root.get(c1);175final T v1 = cv1.getForwardValue(c2);176if (v1 != null) {177return v1;178}179180// Next likely case: it is in the reverse map of c2's BiClassValues181final BiClassValues<T> cv2 = root.get(c2);182final T v2 = cv2.getReverseValue(c1);183if (v2 != null) {184return v2;185}186187// Value is uncached, compute it and cache if possible.188switch (getRetentionDirection(c1, c2)) {189case FORWARD:190// loader of c1 can see loader of c2, store value for (c1, c2) in cv1's forward map191return cv1.computeForward(c2, cy -> compute.apply(c1, cy));192case REVERSE:193// loader of c2 can see loader of c1, store value for (c1, c2) in cv2's reverse map194return cv2.computeReverse(c1, cx -> compute.apply(cx, c2));195case NEITHER:196// Class loaders are unrelated; compute and return uncached.197return compute.apply(c1, c2);198default:199throw new AssertionError(); // enum values exhausted200}201}202203@SuppressWarnings("removal")204private static final AccessControlContext GET_CLASS_LOADER_CONTEXT =205AccessControlContextFactory.createAccessControlContext("getClassLoader");206207@SuppressWarnings("removal")208private static RetentionDirection getRetentionDirection(Class<?> from, Class<?> to) {209return AccessController.doPrivileged((PrivilegedAction<RetentionDirection>) () -> {210final ClassLoader cl1 = from.getClassLoader();211final ClassLoader cl2 = to.getClassLoader();212if (canReferenceDirectly(cl1, cl2)) {213return RetentionDirection.FORWARD;214} else if (canReferenceDirectly(cl2, cl1)) {215return RetentionDirection.REVERSE;216}217return RetentionDirection.NEITHER;218}, GET_CLASS_LOADER_CONTEXT);219}220}221222223