OverloadedDynamicMethod.java revision 1551:f3b883bec2d0
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   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.
39
40   You may not use this file except in compliance with either the Apache
41   License or the BSD License.
42
43   If you choose to use this file in compliance with the Apache License, the
44   following notice applies to you:
45
46       You may obtain a copy of the Apache License at
47
48           http://www.apache.org/licenses/LICENSE-2.0
49
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.
55
56   If you choose to use this file in compliance with the BSD License, the
57   following notice applies to you:
58
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.
70
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*/
83
84package jdk.dynalink.beans;
85
86import java.lang.invoke.MethodHandle;
87import java.lang.invoke.MethodType;
88import java.security.AccessControlContext;
89import java.security.AccessController;
90import java.security.PrivilegedAction;
91import java.text.Collator;
92import java.util.ArrayList;
93import java.util.Collections;
94import java.util.IdentityHashMap;
95import java.util.Iterator;
96import java.util.LinkedList;
97import java.util.List;
98import java.util.Map;
99import java.util.Set;
100import jdk.dynalink.CallSiteDescriptor;
101import jdk.dynalink.beans.ApplicableOverloadedMethods.ApplicabilityTest;
102import jdk.dynalink.internal.AccessControlContextFactory;
103import jdk.dynalink.internal.InternalTypeUtilities;
104import jdk.dynalink.linker.LinkerServices;
105
106/**
107 * Represents a group of {@link SingleDynamicMethod} objects that represents all overloads of a particular name (or all
108 * constructors) for a particular class. Correctly handles overload resolution, variable arity methods, and caller
109 * sensitive methods within the overloads.
110 */
111class OverloadedDynamicMethod extends DynamicMethod {
112    /**
113     * Holds a list of all methods.
114     */
115    private final LinkedList<SingleDynamicMethod> methods;
116    private final ClassLoader classLoader;
117
118    /**
119     * Creates a new overloaded dynamic method.
120     *
121     * @param clazz the class this method belongs to
122     * @param name the name of the method
123     */
124    OverloadedDynamicMethod(final Class<?> clazz, final String name) {
125        this(new LinkedList<SingleDynamicMethod>(), clazz.getClassLoader(), getClassAndMethodName(clazz, name));
126    }
127
128    private OverloadedDynamicMethod(final LinkedList<SingleDynamicMethod> methods, final ClassLoader classLoader, final String name) {
129        super(name);
130        this.methods = methods;
131        this.classLoader = classLoader;
132    }
133
134    @Override
135    SingleDynamicMethod getMethodForExactParamTypes(final String paramTypes) {
136        final LinkedList<SingleDynamicMethod> matchingMethods = new LinkedList<>();
137        for(final SingleDynamicMethod method: methods) {
138            final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes);
139            if(matchingMethod != null) {
140                matchingMethods.add(matchingMethod);
141            }
142        }
143        switch(matchingMethods.size()) {
144            case 0: {
145                return null;
146            }
147            case 1: {
148                return matchingMethods.getFirst();
149            }
150            default: {
151                throw new BootstrapMethodError("Can't choose among " + matchingMethods + " for argument types "
152                        + paramTypes + " for method " + getName());
153            }
154        }
155    }
156
157    @Override
158    public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
159        final MethodType callSiteType = callSiteDescriptor.getMethodType();
160        // First, find all methods applicable to the call site by subtyping (JLS 15.12.2.2)
161        final ApplicableOverloadedMethods subtypingApplicables = getApplicables(callSiteType,
162                ApplicableOverloadedMethods.APPLICABLE_BY_SUBTYPING);
163        // Next, find all methods applicable by method invocation conversion to the call site (JLS 15.12.2.3).
164        final ApplicableOverloadedMethods methodInvocationApplicables = getApplicables(callSiteType,
165                ApplicableOverloadedMethods.APPLICABLE_BY_METHOD_INVOCATION_CONVERSION);
166        // Finally, find all methods applicable by variable arity invocation. (JLS 15.12.2.4).
167        final ApplicableOverloadedMethods variableArityApplicables = getApplicables(callSiteType,
168                ApplicableOverloadedMethods.APPLICABLE_BY_VARIABLE_ARITY);
169
170        // Find the methods that are maximally specific based on the call site signature
171        List<SingleDynamicMethod> maximallySpecifics = subtypingApplicables.findMaximallySpecificMethods();
172        if(maximallySpecifics.isEmpty()) {
173            maximallySpecifics = methodInvocationApplicables.findMaximallySpecificMethods();
174            if(maximallySpecifics.isEmpty()) {
175                maximallySpecifics = variableArityApplicables.findMaximallySpecificMethods();
176            }
177        }
178
179        // Now, get a list of the rest of the methods; those that are *not* applicable to the call site signature based
180        // on JLS rules. As paradoxical as that might sound, we have to consider these for dynamic invocation, as they
181        // might match more concrete types passed in invocations. That's why we provisionally call them "invokables".
182        // This is typical for very generic signatures at call sites. Typical example: call site specifies
183        // (Object, Object), and we have a method whose parameter types are (String, int). None of the JLS applicability
184        // rules will trigger, but we must consider the method, as it can be the right match for a concrete invocation.
185        @SuppressWarnings({ "unchecked", "rawtypes" })
186        final List<SingleDynamicMethod> invokables = (List)methods.clone();
187        invokables.removeAll(subtypingApplicables.getMethods());
188        invokables.removeAll(methodInvocationApplicables.getMethods());
189        invokables.removeAll(variableArityApplicables.getMethods());
190        for(final Iterator<SingleDynamicMethod> it = invokables.iterator(); it.hasNext();) {
191            final SingleDynamicMethod m = it.next();
192            if(!isApplicableDynamically(linkerServices, callSiteType, m)) {
193                it.remove();
194            }
195        }
196
197        // If no additional methods can apply at invocation time, and there's more than one maximally specific method
198        // based on call site signature, that is a link-time ambiguity. In a static scenario, javac would report an
199        // ambiguity error.
200        if(invokables.isEmpty() && maximallySpecifics.size() > 1) {
201            throw new BootstrapMethodError("Can't choose among " + maximallySpecifics + " for argument types "
202                    + callSiteType);
203        }
204
205        // Merge them all.
206        invokables.addAll(maximallySpecifics);
207        switch(invokables.size()) {
208            case 0: {
209                // No overloads can ever match the call site type
210                return null;
211            }
212            case 1: {
213                // Very lucky, we ended up with a single candidate method handle based on the call site signature; we
214                // can link it very simply by delegating to the SingleDynamicMethod.
215                return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
216            }
217            default: {
218                // We have more than one candidate. We have no choice but to link to a method that resolves overloads on
219                // every invocation (alternatively, we could opportunistically link the one method that resolves for the
220                // current arguments, but we'd need to install a fairly complex guard for that and when it'd fail, we'd
221                // go back all the way to candidate selection. Note that we're resolving any potential caller sensitive
222                // methods here to their handles, as the OverloadedMethod instance is specific to a call site, so it
223                // has an already determined Lookup.
224                final List<MethodHandle> methodHandles = new ArrayList<>(invokables.size());
225                for(final SingleDynamicMethod method: invokables) {
226                    methodHandles.add(method.getTarget(callSiteDescriptor));
227                }
228                return new OverloadedMethod(methodHandles, this, getCallSiteClassLoader(callSiteDescriptor), callSiteType, linkerServices).getInvoker();
229            }
230        }
231    }
232
233    private static final AccessControlContext GET_CALL_SITE_CLASS_LOADER_CONTEXT =
234            AccessControlContextFactory.createAccessControlContext(
235                    "getClassLoader", CallSiteDescriptor.GET_LOOKUP_PERMISSION_NAME);
236
237    private static ClassLoader getCallSiteClassLoader(final CallSiteDescriptor callSiteDescriptor) {
238        return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
239            @Override
240            public ClassLoader run() {
241                return callSiteDescriptor.getLookup().lookupClass().getClassLoader();
242            }
243        }, GET_CALL_SITE_CLASS_LOADER_CONTEXT);
244    }
245
246    @Override
247    public boolean contains(final SingleDynamicMethod m) {
248        for(final SingleDynamicMethod method: methods) {
249            if(method.contains(m)) {
250                return true;
251            }
252        }
253        return false;
254    }
255
256    @Override
257    public boolean isConstructor() {
258        assert !methods.isEmpty();
259        return methods.getFirst().isConstructor();
260    }
261
262    @Override
263    public String toString() {
264        // First gather the names and sort them. This makes it consistent and easier to read.
265        final List<String> names = new ArrayList<>(methods.size());
266        int len = 0;
267        for (final SingleDynamicMethod m: methods) {
268            final String name = m.getName();
269            len += name.length();
270            names.add(name);
271        }
272        // Case insensitive sorting, so e.g. "Object" doesn't come before "boolean".
273        final Collator collator = Collator.getInstance();
274        collator.setStrength(Collator.SECONDARY);
275        Collections.sort(names, collator);
276
277        final String className = getClass().getName();
278        // Class name length + length of signatures + 2 chars/per signature for indentation and newline +
279        // 3 for brackets and initial newline
280        final int totalLength = className.length() + len + 2 * names.size() + 3;
281        final StringBuilder b = new StringBuilder(totalLength);
282        b.append('[').append(className).append('\n');
283        for(final String name: names) {
284            b.append(' ').append(name).append('\n');
285        }
286        b.append(']');
287        assert b.length() == totalLength;
288        return b.toString();
289    };
290
291    ClassLoader getClassLoader() {
292        return classLoader;
293    }
294
295    private static boolean isApplicableDynamically(final LinkerServices linkerServices, final MethodType callSiteType,
296            final SingleDynamicMethod m) {
297        final MethodType methodType = m.getMethodType();
298        final boolean varArgs = m.isVarArgs();
299        final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);
300        final int callSiteArgLen = callSiteType.parameterCount();
301
302        // Arity checks
303        if(varArgs) {
304            if(callSiteArgLen < fixedArgLen) {
305                return false;
306            }
307        } else if(callSiteArgLen != fixedArgLen) {
308            return false;
309        }
310
311        // Fixed arguments type checks, starting from 1, as receiver type doesn't participate
312        for(int i = 1; i < fixedArgLen; ++i) {
313            if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), methodType.parameterType(i))) {
314                return false;
315            }
316        }
317        if(!varArgs) {
318            // Not vararg; both arity and types matched.
319            return true;
320        }
321
322        final Class<?> varArgArrayType = methodType.parameterType(fixedArgLen);
323        final Class<?> varArgType = varArgArrayType.getComponentType();
324
325        if(fixedArgLen == callSiteArgLen - 1) {
326            // Exactly one vararg; check both array type matching and array component type matching.
327            final Class<?> callSiteArgType = callSiteType.parameterType(fixedArgLen);
328            return isApplicableDynamically(linkerServices, callSiteArgType, varArgArrayType)
329                    || isApplicableDynamically(linkerServices, callSiteArgType, varArgType);
330        }
331
332        // Either zero, or more than one vararg; check if all actual vararg types match the vararg array component type.
333        for(int i = fixedArgLen; i < callSiteArgLen; ++i) {
334            if(!isApplicableDynamically(linkerServices, callSiteType.parameterType(i), varArgType)) {
335                return false;
336            }
337        }
338
339        return true;
340    }
341
342    private static boolean isApplicableDynamically(final LinkerServices linkerServices, final Class<?> callSiteType,
343            final Class<?> methodType) {
344        return isPotentiallyConvertible(callSiteType, methodType)
345                || linkerServices.canConvert(callSiteType, methodType);
346    }
347
348    private ApplicableOverloadedMethods getApplicables(final MethodType callSiteType, final ApplicabilityTest test) {
349        return new ApplicableOverloadedMethods(methods, callSiteType, test);
350    }
351
352    /**
353     * Add a method to this overloaded method's set.
354     *
355     * @param method a method to add
356     */
357    public void addMethod(final SingleDynamicMethod method) {
358        assert constructorFlagConsistent(method);
359        methods.add(method);
360    }
361
362    private boolean constructorFlagConsistent(final SingleDynamicMethod method) {
363        return methods.isEmpty()? true : (methods.getFirst().isConstructor() == method.isConstructor());
364    }
365
366    /**
367     * Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between
368     * any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as
369     * well as between any primitive type and any reference type that can hold a boxed primitive.
370     *
371     * @param callSiteType the parameter type at the call site
372     * @param methodType the parameter type in the method declaration
373     * @return true if callSiteType is potentially convertible to the methodType.
374     */
375    private static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {
376        // Widening or narrowing reference conversion
377        if(InternalTypeUtilities.areAssignable(callSiteType, methodType)) {
378            return true;
379        }
380        if(callSiteType.isPrimitive()) {
381            // Allow any conversion among primitives, as well as from any
382            // primitive to any type that can receive a boxed primitive.
383            // TODO: narrow this a bit, i.e. allow, say, boolean to Character?
384            // MethodHandles.convertArguments() allows it, so we might need to
385            // too.
386            return methodType.isPrimitive() || isAssignableFromBoxedPrimitive(methodType);
387        }
388        if(methodType.isPrimitive()) {
389            // Allow conversion from any reference type that can contain a
390            // boxed primitive to any primitive.
391            // TODO: narrow this a bit too?
392            return isAssignableFromBoxedPrimitive(callSiteType);
393        }
394        return false;
395    }
396
397    private static final Set<Class<?>> PRIMITIVE_WRAPPER_TYPES = createPrimitiveWrapperTypes();
398
399    private static Set<Class<?>> createPrimitiveWrapperTypes() {
400        final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();
401        addClassHierarchy(classes, Boolean.class);
402        addClassHierarchy(classes, Byte.class);
403        addClassHierarchy(classes, Character.class);
404        addClassHierarchy(classes, Short.class);
405        addClassHierarchy(classes, Integer.class);
406        addClassHierarchy(classes, Long.class);
407        addClassHierarchy(classes, Float.class);
408        addClassHierarchy(classes, Double.class);
409        return classes.keySet();
410    }
411
412    private static void addClassHierarchy(final Map<Class<?>, Class<?>> map, final Class<?> clazz) {
413        if(clazz == null) {
414            return;
415        }
416        map.put(clazz, clazz);
417        addClassHierarchy(map, clazz.getSuperclass());
418        for(final Class<?> itf: clazz.getInterfaces()) {
419            addClassHierarchy(map, itf);
420        }
421    }
422
423    /**
424     * Returns true if the class can be assigned from any boxed primitive.
425     *
426     * @param clazz the class
427     * @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any
428     * primitive wrapper class, or a superclass or superinterface of any primitive wrapper class.
429     */
430    private static boolean isAssignableFromBoxedPrimitive(final Class<?> clazz) {
431        return PRIMITIVE_WRAPPER_TYPES.contains(clazz);
432    }
433}
434