Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/BiClassValue.java
41154 views
1
/*
2
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package jdk.dynalink;
27
28
import java.lang.invoke.MethodHandles;
29
import java.lang.invoke.VarHandle;
30
import java.security.AccessControlContext;
31
import java.security.AccessController;
32
import java.security.PrivilegedAction;
33
import java.util.Map;
34
import java.util.Objects;
35
import java.util.function.BiFunction;
36
import java.util.function.Function;
37
import jdk.dynalink.internal.AccessControlContextFactory;
38
39
import static jdk.dynalink.internal.InternalTypeUtilities.canReferenceDirectly;
40
41
/**
42
* Similar to ClassValue, but lazily associates a computed value with
43
* (potentially) every pair of types.
44
* @param <T> the value to associate with pairs of types.
45
*/
46
final class BiClassValue<T> {
47
/**
48
* Creates a new BiClassValue that uses the specified binary function to
49
* lazily compute the values.
50
* @param compute the binary function to compute the values. Ordinarily, it
51
* is invoked at most once for any pair of values. However,
52
* it is possible for it to be invoked concurrently. It can
53
* even be invoked concurrently multiple times for the same
54
* arguments under contention. In that case, it is undefined
55
* which of the computed values will be retained therefore
56
* returning semantically equivalent values is strongly
57
* recommended; the function should ideally be pure.
58
* Additionally, if the pair of types passed as parameters
59
* are from unrelated class loaders, the computed value is
60
* not cached at all and the function might be reinvoked
61
* with the same parameters in the future. Finally, a null
62
* return value is allowed, but not cached.
63
* @param <T> the type of the values
64
* @return a new BiClassValue that computes the values using the passed
65
* function.
66
*/
67
static <T> BiClassValue<T> computing(final BiFunction<Class<?>, Class<?>, T> compute) {
68
return new BiClassValue<>(compute);
69
}
70
71
/**
72
* A type-specific map that stores the values specific to pairs of types
73
* which include its class in one of the positions of the pair. Internally,
74
* it uses at most two maps named "forward" and "reverse". A BiClassValues
75
* for class C1 can store values for (C1, Cy) in its forward map as well
76
* as values for (Cx, C1) in its reverse map. The reason for this scheme
77
* is to avoid creating unwanted strong references from a parent class
78
* loader to a child class loader. If for a pair of classes (C1, C2)
79
* either C1 and C2 are in the same class loader, or C2 is in parent of C1,
80
* or C2 is a system class, forward map of C1's BiClassValues is used for
81
* storing the computed value. If C1 is in parent of C2, or C1 is a system
82
* class, reverse map of C2's BiClassValues is used for storing. If the
83
* class loaders are unrelated, the computed value is not cached and will
84
* be recomputed on every evaluation.
85
* NOTE that while every instance of this class is type-specific, it does
86
* not store a reference to the type Class object itself. BiClassValuesRoot
87
* creates the association from a type Class object to its BiClassValues'.
88
* @param <T> the type of the values
89
*/
90
private final static class BiClassValues<T> {
91
// These will be used for compareAndExchange on forward and reverse fields.
92
private static final VarHandle FORWARD;
93
private static final VarHandle REVERSE;
94
static {
95
final MethodHandles.Lookup lookup = MethodHandles.lookup();
96
try {
97
FORWARD = lookup.findVarHandle(BiClassValues.class, "forward", Map.class);
98
REVERSE = lookup.findVarHandle(BiClassValues.class, "reverse", Map.class);
99
} catch (NoSuchFieldException | IllegalAccessException e) {
100
throw new AssertionError(e);
101
}
102
}
103
104
private Map<Class<?>, T> forward = Map.of();
105
private Map<Class<?>, T> reverse = Map.of();
106
107
T getForwardValue(final Class<?> c) {
108
return forward.get(c);
109
}
110
111
T getReverseValue(final Class<?> c) {
112
return reverse.get(c);
113
}
114
115
private T compute(final VarHandle mapHandle, final Class<?> c, final Function<Class<?>, T> compute) {
116
@SuppressWarnings("unchecked")
117
Map<Class<?>, T> map = (Map<Class<?>, T>) mapHandle.getVolatile(this);
118
T value;
119
T newValue = null;
120
while ((value = map.get(c)) == null) {
121
if (newValue == null) {
122
newValue = compute.apply(c);
123
if (newValue == null) {
124
break;
125
}
126
}
127
@SuppressWarnings({"unchecked", "rawtypes"})
128
final Map.Entry<Class<?>, T>[] entries = map.entrySet().toArray(new Map.Entry[map.size() + 1]);
129
entries[map.size()] = Map.entry(c, newValue);
130
final var newMap = Map.ofEntries(entries);
131
@SuppressWarnings("unchecked")
132
final var witness = (Map<Class<?>, T>) mapHandle.compareAndExchange(this, map, newMap);
133
if (witness == map) {
134
value = newValue;
135
break;
136
}
137
map = witness;
138
}
139
return value;
140
}
141
142
T computeForward(final Class<?> c, Function<Class<?>, T> compute) {
143
return compute(FORWARD, c, compute);
144
}
145
146
T computeReverse(final Class<?> c, Function<Class<?>, T> compute) {
147
return compute(REVERSE, c, compute);
148
}
149
}
150
151
// A named class used for "root" field so it can be static so it doesn't
152
// gain a synthetic this$0 reference as that'd cause a memory leak through
153
// unwanted anchoring to a GC root when used with system classes.
154
private static final class BiClassValuesRoot<T> extends ClassValue<BiClassValues<T>> {
155
@Override protected BiClassValues<T> computeValue(Class<?> type) {
156
return new BiClassValues<>();
157
}
158
}
159
160
private enum RetentionDirection {
161
FORWARD,
162
REVERSE,
163
NEITHER
164
}
165
166
private final BiClassValuesRoot<T> root = new BiClassValuesRoot<>();
167
private final BiFunction<Class<?>, Class<?>, T> compute;
168
169
private BiClassValue(final BiFunction<Class<?>, Class<?>, T> compute) {
170
this.compute = Objects.requireNonNull(compute);
171
}
172
173
final T get(final Class<?> c1, final Class<?> c2) {
174
// Most likely case: it is in the forward map of c1's BiClassValues
175
final BiClassValues<T> cv1 = root.get(c1);
176
final T v1 = cv1.getForwardValue(c2);
177
if (v1 != null) {
178
return v1;
179
}
180
181
// Next likely case: it is in the reverse map of c2's BiClassValues
182
final BiClassValues<T> cv2 = root.get(c2);
183
final T v2 = cv2.getReverseValue(c1);
184
if (v2 != null) {
185
return v2;
186
}
187
188
// Value is uncached, compute it and cache if possible.
189
switch (getRetentionDirection(c1, c2)) {
190
case FORWARD:
191
// loader of c1 can see loader of c2, store value for (c1, c2) in cv1's forward map
192
return cv1.computeForward(c2, cy -> compute.apply(c1, cy));
193
case REVERSE:
194
// loader of c2 can see loader of c1, store value for (c1, c2) in cv2's reverse map
195
return cv2.computeReverse(c1, cx -> compute.apply(cx, c2));
196
case NEITHER:
197
// Class loaders are unrelated; compute and return uncached.
198
return compute.apply(c1, c2);
199
default:
200
throw new AssertionError(); // enum values exhausted
201
}
202
}
203
204
@SuppressWarnings("removal")
205
private static final AccessControlContext GET_CLASS_LOADER_CONTEXT =
206
AccessControlContextFactory.createAccessControlContext("getClassLoader");
207
208
@SuppressWarnings("removal")
209
private static RetentionDirection getRetentionDirection(Class<?> from, Class<?> to) {
210
return AccessController.doPrivileged((PrivilegedAction<RetentionDirection>) () -> {
211
final ClassLoader cl1 = from.getClassLoader();
212
final ClassLoader cl2 = to.getClassLoader();
213
if (canReferenceDirectly(cl1, cl2)) {
214
return RetentionDirection.FORWARD;
215
} else if (canReferenceDirectly(cl2, cl1)) {
216
return RetentionDirection.REVERSE;
217
}
218
return RetentionDirection.NEITHER;
219
}, GET_CLASS_LOADER_CONTEXT);
220
}
221
}
222
223