Sat, 07 Nov 2020 10:30:02 +0800
Added tag mips-jdk8u275-b01 for changeset 7c756d901f9a
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 */
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
36 Licensed under both the Apache License, Version 2.0 (the "Apache License")
37 and the BSD License (the "BSD License"), with licensee being free to
38 choose either of the two at their discretion.
40 You may not use this file except in compliance with either the Apache
41 License or the BSD License.
43 If you choose to use this file in compliance with the Apache License, the
44 following notice applies to you:
46 You may obtain a copy of the Apache License at
48 http://www.apache.org/licenses/LICENSE-2.0
50 Unless required by applicable law or agreed to in writing, software
51 distributed under the License is distributed on an "AS IS" BASIS,
52 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
53 implied. See the License for the specific language governing
54 permissions and limitations under the License.
56 If you choose to use this file in compliance with the BSD License, the
57 following notice applies to you:
59 Redistribution and use in source and binary forms, with or without
60 modification, are permitted provided that the following conditions are
61 met:
62 * Redistributions of source code must retain the above copyright
63 notice, this list of conditions and the following disclaimer.
64 * Redistributions in binary form must reproduce the above copyright
65 notice, this list of conditions and the following disclaimer in the
66 documentation and/or other materials provided with the distribution.
67 * Neither the name of the copyright holder nor the names of
68 contributors may be used to endorse or promote products derived from
69 this software without specific prior written permission.
71 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
72 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
73 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
74 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
75 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
76 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
77 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
78 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
79 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
80 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
81 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
82 */
84 package jdk.internal.dynalink.beans;
86 import java.lang.invoke.MethodHandle;
87 import java.lang.invoke.MethodHandles;
88 import java.lang.invoke.MethodType;
89 import java.util.ArrayList;
90 import java.util.Iterator;
91 import java.util.List;
92 import java.util.Map;
93 import java.util.concurrent.ConcurrentHashMap;
94 import jdk.internal.dynalink.linker.LinkerServices;
95 import jdk.internal.dynalink.support.Lookup;
96 import jdk.internal.dynalink.support.TypeUtilities;
98 /**
99 * Represents a subset of overloaded methods for a certain method name on a certain class. It can be either a fixarg or
100 * a vararg subset depending on the subclass. The method is for a fixed number of arguments though (as it is generated
101 * for a concrete call site). As such, all methods in the subset can be invoked with the specified number of arguments
102 * (exactly matching for fixargs, or having less than or equal fixed arguments, for varargs).
103 *
104 * @author Attila Szegedi
105 */
106 class OverloadedMethod {
107 private final Map<ClassString, MethodHandle> argTypesToMethods = new ConcurrentHashMap<>();
108 private final OverloadedDynamicMethod parent;
109 private final MethodType callSiteType;
110 private final MethodHandle invoker;
111 private final LinkerServices linkerServices;
112 private final ArrayList<MethodHandle> fixArgMethods;
113 private final ArrayList<MethodHandle> varArgMethods;
115 OverloadedMethod(final List<MethodHandle> methodHandles, final OverloadedDynamicMethod parent, final MethodType callSiteType,
116 final LinkerServices linkerServices) {
117 this.parent = parent;
118 final Class<?> commonRetType = getCommonReturnType(methodHandles);
119 this.callSiteType = callSiteType.changeReturnType(commonRetType);
120 this.linkerServices = linkerServices;
122 fixArgMethods = new ArrayList<>(methodHandles.size());
123 varArgMethods = new ArrayList<>(methodHandles.size());
124 final int argNum = callSiteType.parameterCount();
125 for(MethodHandle mh: methodHandles) {
126 if(mh.isVarargsCollector()) {
127 final MethodHandle asFixed = mh.asFixedArity();
128 if(argNum == asFixed.type().parameterCount()) {
129 fixArgMethods.add(asFixed);
130 }
131 varArgMethods.add(mh);
132 } else {
133 fixArgMethods.add(mh);
134 }
135 }
136 fixArgMethods.trimToSize();
137 varArgMethods.trimToSize();
139 final MethodHandle bound = SELECT_METHOD.bindTo(this);
140 final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
141 callSiteType.changeReturnType(MethodHandle.class));
142 invoker = linkerServices.asTypeLosslessReturn(MethodHandles.foldArguments(
143 MethodHandles.exactInvoker(this.callSiteType), collecting), callSiteType);
144 }
146 MethodHandle getInvoker() {
147 return invoker;
148 }
150 private static final MethodHandle SELECT_METHOD = Lookup.findOwnSpecial(MethodHandles.lookup(), "selectMethod",
151 MethodHandle.class, Object[].class);
153 @SuppressWarnings("unused")
154 private MethodHandle selectMethod(final Object[] args) throws NoSuchMethodException {
155 final Class<?>[] argTypes = new Class<?>[args.length];
156 for(int i = 0; i < argTypes.length; ++i) {
157 final Object arg = args[i];
158 argTypes[i] = arg == null ? ClassString.NULL_CLASS : arg.getClass();
159 }
160 final ClassString classString = new ClassString(argTypes);
161 MethodHandle method = argTypesToMethods.get(classString);
162 if(method == null) {
163 List<MethodHandle> methods = classString.getMaximallySpecifics(fixArgMethods, linkerServices, false);
164 if(methods.isEmpty()) {
165 methods = classString.getMaximallySpecifics(varArgMethods, linkerServices, true);
166 }
167 switch(methods.size()) {
168 case 0: {
169 method = getNoSuchMethodThrower(argTypes);
170 break;
171 }
172 case 1: {
173 method = SingleDynamicMethod.getInvocation(methods.get(0), callSiteType, linkerServices);
174 break;
175 }
176 default: {
177 // This is unfortunate - invocation time ambiguity. We can still save the day if
178 method = getAmbiguousMethodThrower(argTypes, methods);
179 break;
180 }
181 }
182 // Avoid keeping references to unrelated classes; this ruins the performance a bit, but avoids class loader
183 // memory leaks.
184 if(classString.isVisibleFrom(parent.getClassLoader())) {
185 argTypesToMethods.put(classString, method);
186 }
187 }
188 return method;
189 }
191 private MethodHandle getNoSuchMethodThrower(final Class<?>[] argTypes) {
192 return adaptThrower(MethodHandles.insertArguments(THROW_NO_SUCH_METHOD, 0, this, argTypes));
193 }
195 private static final MethodHandle THROW_NO_SUCH_METHOD = Lookup.findOwnSpecial(MethodHandles.lookup(),
196 "throwNoSuchMethod", void.class, Class[].class);
198 @SuppressWarnings("unused")
199 private void throwNoSuchMethod(final Class<?>[] argTypes) throws NoSuchMethodException {
200 if(varArgMethods.isEmpty()) {
201 throw new NoSuchMethodException("None of the fixed arity signatures " + getSignatureList(fixArgMethods) +
202 " of method " + parent.getName() + " match the argument types " + argTypesString(argTypes));
203 }
204 throw new NoSuchMethodException("None of the fixed arity signatures " + getSignatureList(fixArgMethods) +
205 " or the variable arity signatures " + getSignatureList(varArgMethods) + " of the method " +
206 parent.getName() + " match the argument types " + argTypesString(argTypes));
207 }
209 private MethodHandle getAmbiguousMethodThrower(final Class<?>[] argTypes, final List<MethodHandle> methods) {
210 return adaptThrower(MethodHandles.insertArguments(THROW_AMBIGUOUS_METHOD, 0, this, argTypes, methods));
211 }
213 private MethodHandle adaptThrower(final MethodHandle rawThrower) {
214 return MethodHandles.dropArguments(rawThrower, 0, callSiteType.parameterList()).asType(callSiteType);
215 }
217 private static final MethodHandle THROW_AMBIGUOUS_METHOD = Lookup.findOwnSpecial(MethodHandles.lookup(),
218 "throwAmbiguousMethod", void.class, Class[].class, List.class);
220 @SuppressWarnings("unused")
221 private void throwAmbiguousMethod(final Class<?>[] argTypes, final List<MethodHandle> methods) throws NoSuchMethodException {
222 final String arity = methods.get(0).isVarargsCollector() ? "variable" : "fixed";
223 throw new NoSuchMethodException("Can't unambiguously select between " + arity + " arity signatures " +
224 getSignatureList(methods) + " of the method " + parent.getName() + " for argument types " +
225 argTypesString(argTypes));
226 }
228 private static String argTypesString(final Class<?>[] classes) {
229 final StringBuilder b = new StringBuilder().append('[');
230 appendTypes(b, classes, false);
231 return b.append(']').toString();
232 }
234 private static String getSignatureList(final List<MethodHandle> methods) {
235 final StringBuilder b = new StringBuilder().append('[');
236 final Iterator<MethodHandle> it = methods.iterator();
237 if(it.hasNext()) {
238 appendSig(b, it.next());
239 while(it.hasNext()) {
240 appendSig(b.append(", "), it.next());
241 }
242 }
243 return b.append(']').toString();
244 }
246 private static void appendSig(final StringBuilder b, final MethodHandle m) {
247 b.append('(');
248 appendTypes(b, m.type().parameterArray(), m.isVarargsCollector());
249 b.append(')');
250 }
252 private static void appendTypes(final StringBuilder b, final Class<?>[] classes, final boolean varArg) {
253 final int l = classes.length;
254 if(!varArg) {
255 if(l > 1) {
256 b.append(classes[1].getCanonicalName());
257 for(int i = 2; i < l; ++i) {
258 b.append(", ").append(classes[i].getCanonicalName());
259 }
260 }
261 } else {
262 for(int i = 1; i < l - 1; ++i) {
263 b.append(classes[i].getCanonicalName()).append(", ");
264 }
265 b.append(classes[l - 1].getComponentType().getCanonicalName()).append("...");
266 }
267 }
269 private static Class<?> getCommonReturnType(final List<MethodHandle> methodHandles) {
270 final Iterator<MethodHandle> it = methodHandles.iterator();
271 Class<?> retType = it.next().type().returnType();
272 while(it.hasNext()) {
273 retType = TypeUtilities.getCommonLosslessConversionType(retType, it.next().type().returnType());
274 }
275 return retType;
276 }
277 }