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/SingleDynamicMethod.java
41161 views
1
/*
2
* Copyright (c) 2010, 2013, 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.MethodHandles;
65
import java.lang.invoke.MethodType;
66
import java.lang.reflect.Array;
67
import java.util.StringTokenizer;
68
import jdk.dynalink.CallSiteDescriptor;
69
import jdk.dynalink.linker.LinkerServices;
70
import jdk.dynalink.linker.support.Guards;
71
import jdk.dynalink.linker.support.Lookup;
72
73
/**
74
* Base class for dynamic methods that dispatch to a single target Java method or constructor. Handles adaptation of the
75
* target method to a call site type (including mapping variable arity methods to a call site signature with different
76
* arity).
77
*/
78
abstract class SingleDynamicMethod extends DynamicMethod {
79
private static final MethodHandle CAN_CONVERT_TO = Lookup.findOwnStatic(MethodHandles.lookup(), "canConvertTo", boolean.class, LinkerServices.class, Class.class, Object.class);
80
81
SingleDynamicMethod(final String name) {
82
super(name);
83
}
84
85
/**
86
* Returns true if this method is variable arity.
87
* @return true if this method is variable arity.
88
*/
89
abstract boolean isVarArgs();
90
91
/**
92
* Returns this method's native type.
93
* @return this method's native type.
94
*/
95
abstract MethodType getMethodType();
96
97
/**
98
* Given a specified call site descriptor, returns a method handle to this method's target. The target
99
* should only depend on the descriptor's lookup, and it should only retrieve it (as a privileged
100
* operation) when it is absolutely needed.
101
* @param desc the call site descriptor to use.
102
* @return the handle to this method's target method.
103
*/
104
abstract MethodHandle getTarget(CallSiteDescriptor desc);
105
106
@Override
107
MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
108
return linkerServices.getWithLookup(()->getInvocation(getTarget(callSiteDescriptor),
109
callSiteDescriptor.getMethodType(), linkerServices), callSiteDescriptor);
110
}
111
112
@Override
113
SingleDynamicMethod getMethodForExactParamTypes(final String paramTypes) {
114
return typeMatchesDescription(paramTypes, getMethodType()) ? this : null;
115
}
116
117
@Override
118
boolean contains(final SingleDynamicMethod method) {
119
return getMethodType().parameterList().equals(method.getMethodType().parameterList());
120
}
121
122
static String getMethodNameWithSignature(final MethodType type, final String methodName, final boolean withReturnType) {
123
final String typeStr = type.toString();
124
final int retTypeIndex = typeStr.lastIndexOf(')') + 1;
125
int secondParamIndex = typeStr.indexOf(',') + 1;
126
if(secondParamIndex == 0) {
127
secondParamIndex = retTypeIndex - 1;
128
}
129
final StringBuilder b = new StringBuilder();
130
if (withReturnType) {
131
b.append(typeStr, retTypeIndex, typeStr.length()).append(' ');
132
}
133
return b.append(methodName).append('(').append(typeStr, secondParamIndex, retTypeIndex).toString();
134
}
135
136
/**
137
* Given a method handle and a call site type, adapts the method handle to the call site type. Performs type
138
* conversions as needed using the specified linker services, and in case that the method handle is a vararg
139
* collector, matches it to the arity of the call site. The type of the return value is only changed if it can be
140
* converted using a conversion that loses neither precision nor magnitude, see
141
* {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
142
* @param target the method handle to adapt
143
* @param callSiteType the type of the call site
144
* @param linkerServices the linker services used for type conversions
145
* @return the adapted method handle.
146
*/
147
static MethodHandle getInvocation(final MethodHandle target, final MethodType callSiteType, final LinkerServices linkerServices) {
148
final MethodHandle filteredTarget = linkerServices.filterInternalObjects(target);
149
final MethodType methodType = filteredTarget.type();
150
final int paramsLen = methodType.parameterCount();
151
final boolean varArgs = target.isVarargsCollector();
152
final MethodHandle fixTarget = varArgs ? filteredTarget.asFixedArity() : filteredTarget;
153
final int fixParamsLen = varArgs ? paramsLen - 1 : paramsLen;
154
final int argsLen = callSiteType.parameterCount();
155
if(argsLen < fixParamsLen) {
156
// Less actual arguments than number of fixed declared arguments; can't invoke.
157
return null;
158
}
159
// Method handle has the same number of fixed arguments as the call site type
160
if(argsLen == fixParamsLen) {
161
// Method handle that matches the number of actual arguments as the number of fixed arguments
162
final MethodHandle matchedMethod;
163
if(varArgs) {
164
// If vararg, add a zero-length array of the expected type as the last argument to signify no variable
165
// arguments.
166
matchedMethod = MethodHandles.insertArguments(fixTarget, fixParamsLen, Array.newInstance(
167
methodType.parameterType(fixParamsLen).getComponentType(), 0));
168
} else {
169
// Otherwise, just use the method
170
matchedMethod = fixTarget;
171
}
172
return createConvertingInvocation(matchedMethod, linkerServices, callSiteType);
173
}
174
175
// What's below only works for varargs
176
if(!varArgs) {
177
return null;
178
}
179
180
final Class<?> varArgType = methodType.parameterType(fixParamsLen);
181
// Handle a somewhat sinister corner case: caller passes exactly one argument in the vararg position, and we
182
// must handle both a prepacked vararg array as well as a genuine 1-long vararg sequence.
183
if(argsLen == paramsLen) {
184
final Class<?> callSiteLastArgType = callSiteType.parameterType(fixParamsLen);
185
if(varArgType.isAssignableFrom(callSiteLastArgType)) {
186
// Call site signature guarantees we'll always be passed a single compatible array; just link directly
187
// to the method, introducing necessary conversions. Also, preserve it being a variable arity method.
188
return createConvertingInvocation(filteredTarget, linkerServices, callSiteType).asVarargsCollector(
189
callSiteLastArgType);
190
}
191
192
// This method handle takes the single argument and packs it into a newly allocated single-element array. It
193
// will be used when the incoming argument can't be converted to the vararg array type (the "vararg packer"
194
// method).
195
final MethodHandle varArgCollectingInvocation = createConvertingInvocation(collectArguments(fixTarget,
196
argsLen), linkerServices, callSiteType);
197
198
// Is call site type assignable from an array type (e.g. Object:int[], or Object[]:String[])
199
final boolean isAssignableFromArray = callSiteLastArgType.isAssignableFrom(varArgType);
200
// Do we have a custom conversion that can potentially convert the call site type to an array?
201
final boolean isCustomConvertible = linkerServices.canConvert(callSiteLastArgType, varArgType);
202
if(!isAssignableFromArray && !isCustomConvertible) {
203
// Call site signature guarantees the argument can definitely not be converted to an array (i.e. it is
204
// primitive), and no conversion can help with it either. Link immediately to a vararg-packing method
205
// handle.
206
return varArgCollectingInvocation;
207
}
208
209
// This method handle employs language-specific conversions to convert the last argument into an array of
210
// vararg type.
211
final MethodHandle arrayConvertingInvocation = createConvertingInvocation(MethodHandles.filterArguments(
212
fixTarget, fixParamsLen, linkerServices.getTypeConverter(callSiteLastArgType, varArgType)),
213
linkerServices, callSiteType);
214
215
// This method handle determines whether the value can be converted to the array of vararg type using a
216
// language-specific conversion.
217
final MethodHandle canConvertArgToArray = MethodHandles.insertArguments(CAN_CONVERT_TO, 0, linkerServices,
218
varArgType);
219
220
// This one adjusts the previous one for the location of the argument and the call site type.
221
final MethodHandle canConvertLastArgToArray = MethodHandles.dropArguments(canConvertArgToArray, 0,
222
MethodType.genericMethodType(fixParamsLen).parameterList()).asType(callSiteType.changeReturnType(boolean.class));
223
224
// This one takes the previous ones and combines them into a method handle that converts the argument into
225
// a vararg array when it can, otherwise falls back to the vararg packer.
226
final MethodHandle convertToArrayWhenPossible = MethodHandles.guardWithTest(canConvertLastArgToArray,
227
arrayConvertingInvocation, varArgCollectingInvocation);
228
229
if(isAssignableFromArray) {
230
return MethodHandles.guardWithTest(
231
// Is incoming parameter already a compatible array?
232
Guards.isInstance(varArgType, fixParamsLen, callSiteType),
233
// Yes: just pass it to the method
234
createConvertingInvocation(fixTarget, linkerServices, callSiteType),
235
// No: either go through a custom conversion, or if it is not possible, go directly to the
236
// vararg packer.
237
isCustomConvertible ? convertToArrayWhenPossible : varArgCollectingInvocation);
238
}
239
240
// Just do the custom conversion with fallback to the vararg packer logic.
241
assert isCustomConvertible;
242
return convertToArrayWhenPossible;
243
}
244
245
// Remaining case: more than one vararg.
246
return createConvertingInvocation(collectArguments(fixTarget, argsLen), linkerServices, callSiteType);
247
}
248
249
@SuppressWarnings("unused")
250
private static boolean canConvertTo(final LinkerServices linkerServices, final Class<?> to, final Object obj) {
251
return obj != null && linkerServices.canConvert(obj.getClass(), to);
252
}
253
254
/**
255
* Creates a method handle out of the original target that will collect the varargs for the exact component type of
256
* the varArg array. Note that this will nicely trigger language-specific type converters for exactly those varargs
257
* for which it is necessary when later passed to linkerServices.convertArguments().
258
*
259
* @param target the original method handle
260
* @param parameterCount the total number of arguments in the new method handle
261
* @return a collecting method handle
262
*/
263
static MethodHandle collectArguments(final MethodHandle target, final int parameterCount) {
264
final MethodType methodType = target.type();
265
final int fixParamsLen = methodType.parameterCount() - 1;
266
final Class<?> arrayType = methodType.parameterType(fixParamsLen);
267
return target.asCollector(arrayType, parameterCount - fixParamsLen);
268
}
269
270
private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
271
final LinkerServices linkerServices, final MethodType callSiteType) {
272
return linkerServices.asTypeLosslessReturn(sizedMethod, callSiteType);
273
}
274
275
private static boolean typeMatchesDescription(final String paramTypes, final MethodType type) {
276
final StringTokenizer tok = new StringTokenizer(paramTypes, ", ");
277
for(int i = 1; i < type.parameterCount(); ++i) { // i = 1 as we ignore the receiver
278
if(!(tok.hasMoreTokens() && typeNameMatches(tok.nextToken(), type.parameterType(i)))) {
279
return false;
280
}
281
}
282
return !tok.hasMoreTokens();
283
}
284
285
private static boolean typeNameMatches(final String typeName, final Class<?> type) {
286
return typeName.equals(typeName.indexOf('.') == -1 ? type.getSimpleName() : type.getCanonicalName());
287
}
288
}
289
290