Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.dynalink/share/classes/jdk/dynalink/beans/OverloadedDynamicMethod.java
41161 views
1
/*
2
* Copyright (c) 2010, 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
/*
27
* This file is available under and governed by the GNU General Public
28
* License version 2 only, as published by the Free Software Foundation.
29
* However, the following notice accompanied the original version of this
30
* file, and Oracle licenses the original version of this file under the BSD
31
* license:
32
*/
33
/*
34
Copyright 2009-2013 Attila Szegedi
35
36
Redistribution and use in source and binary forms, with or without
37
modification, are permitted provided that the following conditions are
38
met:
39
* Redistributions of source code must retain the above copyright
40
notice, this list of conditions and the following disclaimer.
41
* Redistributions in binary form must reproduce the above copyright
42
notice, this list of conditions and the following disclaimer in the
43
documentation and/or other materials provided with the distribution.
44
* Neither the name of the copyright holder nor the names of
45
contributors may be used to endorse or promote products derived from
46
this software without specific prior written permission.
47
48
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
49
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
51
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
52
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
55
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
56
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
57
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
58
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
59
*/
60
61
package jdk.dynalink.beans;
62
63
import java.lang.invoke.MethodHandle;
64
import java.lang.invoke.MethodType;
65
import java.security.AccessControlContext;
66
import java.security.AccessController;
67
import java.security.PrivilegedAction;
68
import java.text.Collator;
69
import java.util.ArrayList;
70
import java.util.IdentityHashMap;
71
import java.util.LinkedList;
72
import java.util.List;
73
import java.util.Map;
74
import java.util.Set;
75
import jdk.dynalink.CallSiteDescriptor;
76
import jdk.dynalink.SecureLookupSupplier;
77
import jdk.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest;
78
import jdk.dynalink.internal.AccessControlContextFactory;
79
import jdk.dynalink.internal.InternalTypeUtilities;
80
import jdk.dynalink.linker.LinkerServices;
81
82
/**
83
* Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all
84
* constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller
85
* sensitive methods within the overloads.
86
*/
87
class OverloadedDynamicMethod extends DynamicMethod {
88
/**
89
* Holds a list of all methods.
90
*/
91
private final LinkedList<SingleDynamicMethod> methods = new LinkedList<>();
92
93
/**
94
* Creates a new overloaded dynamic method.
95
*
96
* @param clazz the class this method belongs to
97
* @param name the name of the method
98
*/
99
OverloadedDynamicMethod(final Class<?> clazz, final String name) {
100
super(getClassAndMethodName(clazz, name));
101
}
102
103
@Override
104
SingleDynamicMethod getMethodForExactParamTypes(final String paramTypes) {
105
final LinkedList<SingleDynamicMethod> matchingMethods = new LinkedList<>();
106
for(final SingleDynamicMethod method: methods) {
107
final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes);
108
if(matchingMethod != null) {
109
matchingMethods.add(matchingMethod);
110
}
111
}
112
switch(matchingMethods.size()) {
113
case 0: {
114
return null;
115
}
116
case 1: {
117
return matchingMethods.getFirst();
118
}
119
default: {
120
throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types "
121
+ paramTypes + " for method " + getName());
122
}
123
}
124
}
125
126
@Override
127
MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
128
final MethodType callSiteType = callSiteDescriptor.getMethodType();
129
// First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2)
130
final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType,
131
ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING);
132
// Next, find all methods applicable by method invocation conversion to the call site (JLS 15.12.2.3).
133
final ApplicableOverloadedMethods methodInvocationApplicables = getApplicables(callSiteType,
134
ApplicableOverloadedMethods.APPLICABLE_BY_METHOD_INVOCATION_CONVERSION);
135
// Finally, find all methods applicable by variable arity invocation. (JLS 15.12.2.4).
136
final ApplicableOverloadedMethods variableArityApplicables = getApplicables(callSiteType,
137
ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY);
138
139
// Find the methods that are maximally specific based on the call site signature
140
List<SingleDynamicMethod> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();
141
if(maximallySpecifics.isEmpty()) {
142
maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods();
143
if(maximallySpecifics.isEmpty()) {
144
maximallySpecifics = variableArityApplicables.findMaximallySpecificMethods();
145
}
146
}
147
148
// Now, get a list of the rest of the methods; those that are *not* applicable to the call site signature based
149
// on JLS rules. As paradoxical as that might sound, we have to consider these for dynamic invocation, as they
150
// might match more concrete types passed in invocations. That's why we provisionally call them "invokables".
151
// This is typical for very generic signatures at call sites. Typical example: call site specifies
152
// (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability
153
// rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation.
154
@SuppressWarnings({ "unchecked", "rawtypes" })
155
final List<SingleDynamicMethod> invokables = (List)methods.clone();
156
invokables.removeAll(subtypingApplicables.getMethods());
157
invokables.removeAll(methodInvocationApplicables.getMethods());
158
invokables.removeAll(variableArityApplicables.getMethods());
159
invokables.removeIf(m -> !isApplicableDynamically(linkerServices, callSiteType, m));
160
161
// If no additional methods can apply at invocation time, and there's more than one maximally specific method
162
// based on call site signature, that is a link-time ambiguity. In a static scenario, javac would report an
163
// ambiguity error.
164
if(invokables.isEmpty() && maximallySpecifics.size() > 1) {
165
throw new BootstrapMethodError("Can't choose among " + maximallySpecifics + " for argument types "
166
+ callSiteType);
167
}
168
169
// Merge them all.
170
invokables.addAll(maximallySpecifics);
171
switch(invokables.size()) {
172
case 0: {
173
// No overloads can ever match the call site type
174
return null;
175
}
176
case 1: {
177
// Very lucky, we ended up with a single candidate method handle based on the call site signature; we
178
// can link it very simply by delegating to the SingleDynamicMethod.
179
return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
180
}
181
default: {
182
// We have more than one candidate. We have no choice but to link to a method that resolves overloads on
183
// every invocation (alternatively, we could opportunistically link the one method that resolves for the
184
// current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd
185
// go back all the way to candidate selection. Note that we're resolving any potential caller sensitive
186
// methods here to their handles, as the OverloadedMethod instance is specific to a call site, so it
187
// has an already determined Lookup.
188
final List<MethodHandle> methodHandles = new ArrayList<>(invokables.size());
189
for(final SingleDynamicMethod method: invokables) {
190
methodHandles.add(method.getTarget(callSiteDescriptor));
191
}
192
return new OverloadedMethod(methodHandles, this, getCallSiteClassLoader(callSiteDescriptor), callSiteType, linkerServices, callSiteDescriptor).getInvoker();
193
}
194
}
195
}
196
197
@SuppressWarnings("removal")
198
private static final AccessControlContext GET_CALL_SITE_CLASS_LOADER_CONTEXT =
199
AccessControlContextFactory.createAccessControlContext(
200
"getClassLoader", SecureLookupSupplier.GET_LOOKUP_PERMISSION_NAME);
201
202
@SuppressWarnings("removal")
203
private static ClassLoader getCallSiteClassLoader(final CallSiteDescriptor callSiteDescriptor) {
204
return AccessController.doPrivileged(
205
(PrivilegedAction<ClassLoader>) () -> callSiteDescriptor.getLookup().lookupClass().getClassLoader(),
206
GET_CALL_SITE_CLASS_LOADER_CONTEXT);
207
}
208
209
@Override
210
public boolean contains(final SingleDynamicMethod m) {
211
for(final SingleDynamicMethod method: methods) {
212
if(method.contains(m)) {
213
return true;
214
}
215
}
216
return false;
217
}
218
219
@Override
220
public boolean isConstructor() {
221
assert !methods.isEmpty();
222
return methods.getFirst().isConstructor();
223
}
224
225
@Override
226
public String toString() {
227
// First gather the names and sort them. This makes it consistent and easier to read.
228
final List<String> names = new ArrayList<>(methods.size());
229
int len = 0;
230
for (final SingleDynamicMethod m: methods) {
231
final String name = m.getName();
232
len += name.length();
233
names.add(name);
234
}
235
// Case insensitive sorting, so e.g. "Object" doesn't come before "boolean".
236
final Collator collator = Collator.getInstance();
237
collator.setStrength(Collator.SECONDARY);
238
names.sort(collator);
239
240
final String className = getClass().getName();
241
// Class name length + length of signatures + 2 chars/per signature for indentation and newline +
242
// 3 for brackets and initial newline
243
final int totalLength = className.length() + len + 2 * names.size() + 3;
244
final StringBuilder b = new StringBuilder(totalLength);
245
b.append('[').append(className).append('\n');
246
for(final String name: names) {
247
b.append(' ').append(name).append('\n');
248
}
249
b.append(']');
250
assert b.length() == totalLength;
251
return b.toString();
252
}
253
254
private static boolean isApplicableDynamically(final LinkerServices linkerServices, final MethodType callSiteType,
255
final SingleDynamicMethod m) {
256
final MethodType methodType = m.getMethodType();
257
final boolean varArgs = m.isVarArgs();
258
final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);
259
final int callSiteArgLen = callSiteType.parameterCount();
260
261
// Arity checks
262
if(varArgs) {
263
if(callSiteArgLen < fixedArgLen) {
264
return false;
265
}
266
} else if(callSiteArgLen != fixedArgLen) {
267
return false;
268
}
269
270
// Fixed arguments type checks, starting from 1, as receiver type doesn't participate
271
for(int i = 1; i < fixedArgLen; ++i) {
272
if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), methodType.parameterType(i))) {
273
return false;
274
}
275
}
276
if(!varArgs) {
277
// Not vararg; both arity and types matched.
278
return true;
279
}
280
281
final Class<?> varArgArrayType = methodType.parameterType(fixedArgLen);
282
final Class<?> varArgType = varArgArrayType.getComponentType();
283
284
if(fixedArgLen == callSiteArgLen - 1) {
285
// Exactly one vararg; check both array type matching and array component type matching.
286
final Class<?> callSiteArgType = callSiteType.parameterType(fixedArgLen);
287
return isApplicableDynamically(linkerServices, callSiteArgType, varArgArrayType)
288
|| isApplicableDynamically(linkerServices, callSiteArgType, varArgType);
289
}
290
291
// Either zero, or more than one vararg; check if all actual vararg types match the vararg array component type.
292
for(int i = fixedArgLen; i < callSiteArgLen; ++i) {
293
if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), varArgType)) {
294
return false;
295
}
296
}
297
298
return true;
299
}
300
301
private static boolean isApplicableDynamically(final LinkerServices linkerServices, final Class<?> callSiteType,
302
final Class<?> methodType) {
303
return isPotentiallyConvertible(callSiteType, methodType)
304
|| linkerServices.canConvert(callSiteType, methodType);
305
}
306
307
private ApplicableOverloadedMethods getApplicables(final MethodType callSiteType, final ApplicabilityTest test) {
308
return new ApplicableOverloadedMethods(methods, callSiteType, test);
309
}
310
311
/**
312
* Add a method to this overloaded method's set.
313
*
314
* @param method a method to add
315
*/
316
public void addMethod(final SingleDynamicMethod method) {
317
assert constructorFlagConsistent(method);
318
methods.add(method);
319
}
320
321
private boolean constructorFlagConsistent(final SingleDynamicMethod method) {
322
return methods.isEmpty() || methods.getFirst().isConstructor() == method.isConstructor();
323
}
324
325
/**
326
* Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between
327
* any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as
328
* well as between any primitive type and any reference type that can hold a boxed primitive.
329
*
330
* @param callSiteType the parameter type at the call site
331
* @param methodType the parameter type in the method declaration
332
* @return true if callSiteType is potentially convertible to the methodType.
333
*/
334
private static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {
335
// Widening or narrowing reference conversion
336
if(InternalTypeUtilities.areAssignable(callSiteType, methodType)) {
337
return true;
338
}
339
if(callSiteType.isPrimitive()) {
340
// Allow any conversion among primitives, as well as from any
341
// primitive to any type that can receive a boxed primitive.
342
// TODO: narrow this a bit, i.e. allow, say, boolean to Character?
343
// MethodHandles.convertArguments() allows it, so we might need to
344
// too.
345
return methodType.isPrimitive() || isAssignableFromBoxedPrimitive(methodType);
346
}
347
if(methodType.isPrimitive()) {
348
// Allow conversion from any reference type that can contain a
349
// boxed primitive to any primitive.
350
// TODO: narrow this a bit too?
351
return isAssignableFromBoxedPrimitive(callSiteType);
352
}
353
return false;
354
}
355
356
private static final Set<Class<?>> PRIMITIVE_WRAPPER_TYPES = createPrimitiveWrapperTypes();
357
358
private static Set<Class<?>> createPrimitiveWrapperTypes() {
359
final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();
360
addClassHierarchy(classes, Boolean.class);
361
addClassHierarchy(classes, Byte.class);
362
addClassHierarchy(classes, Character.class);
363
addClassHierarchy(classes, Short.class);
364
addClassHierarchy(classes, Integer.class);
365
addClassHierarchy(classes, Long.class);
366
addClassHierarchy(classes, Float.class);
367
addClassHierarchy(classes, Double.class);
368
return classes.keySet();
369
}
370
371
private static void addClassHierarchy(final Map<Class<?>, Class<?>> map, final Class<?> clazz) {
372
if(clazz == null) {
373
return;
374
}
375
map.put(clazz, clazz);
376
addClassHierarchy(map, clazz.getSuperclass());
377
for(final Class<?> itf: clazz.getInterfaces()) {
378
addClassHierarchy(map, itf);
379
}
380
}
381
382
/**
383
* Returns true if the class can be assigned from any boxed primitive.
384
*
385
* @param clazz the class
386
* @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any
387
* primitive wrapper class, or a superclass or superinterface of any primitive wrapper class.
388
*/
389
private static boolean isAssignableFromBoxedPrimitive(final Class<?> clazz) {
390
return PRIMITIVE_WRAPPER_TYPES.contains(clazz);
391
}
392
}
393
394