aoqi@0: /* aoqi@0: * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: /* aoqi@0: * This file is available under and governed by the GNU General Public aoqi@0: * License version 2 only, as published by the Free Software Foundation. aoqi@0: * However, the following notice accompanied the original version of this aoqi@0: * file, and Oracle licenses the original version of this file under the BSD aoqi@0: * license: aoqi@0: */ aoqi@0: /* aoqi@0: Copyright 2009-2013 Attila Szegedi aoqi@0: aoqi@0: Licensed under both the Apache License, Version 2.0 (the "Apache License") aoqi@0: and the BSD License (the "BSD License"), with licensee being free to aoqi@0: choose either of the two at their discretion. aoqi@0: aoqi@0: You may not use this file except in compliance with either the Apache aoqi@0: License or the BSD License. aoqi@0: aoqi@0: If you choose to use this file in compliance with the Apache License, the aoqi@0: following notice applies to you: aoqi@0: aoqi@0: You may obtain a copy of the Apache License at aoqi@0: aoqi@0: http://www.apache.org/licenses/LICENSE-2.0 aoqi@0: aoqi@0: Unless required by applicable law or agreed to in writing, software aoqi@0: distributed under the License is distributed on an "AS IS" BASIS, aoqi@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or aoqi@0: implied. See the License for the specific language governing aoqi@0: permissions and limitations under the License. aoqi@0: aoqi@0: If you choose to use this file in compliance with the BSD License, the aoqi@0: following notice applies to you: aoqi@0: aoqi@0: Redistribution and use in source and binary forms, with or without aoqi@0: modification, are permitted provided that the following conditions are aoqi@0: met: aoqi@0: * Redistributions of source code must retain the above copyright aoqi@0: notice, this list of conditions and the following disclaimer. aoqi@0: * Redistributions in binary form must reproduce the above copyright aoqi@0: notice, this list of conditions and the following disclaimer in the aoqi@0: documentation and/or other materials provided with the distribution. aoqi@0: * Neither the name of the copyright holder nor the names of aoqi@0: contributors may be used to endorse or promote products derived from aoqi@0: this software without specific prior written permission. aoqi@0: aoqi@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS aoqi@0: IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED aoqi@0: TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A aoqi@0: PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER aoqi@0: BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR aoqi@0: CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF aoqi@0: SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR aoqi@0: BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, aoqi@0: WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR aoqi@0: OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF aoqi@0: ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. aoqi@0: */ aoqi@0: aoqi@0: package jdk.internal.dynalink.support; aoqi@0: aoqi@0: import java.util.ArrayList; aoqi@0: import java.util.Collection; aoqi@0: import java.util.Collections; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.HashSet; aoqi@0: import java.util.IdentityHashMap; aoqi@0: import java.util.Iterator; aoqi@0: import java.util.List; aoqi@0: import java.util.Map; aoqi@0: import java.util.Set; aoqi@0: aoqi@0: /** aoqi@0: * Various static utility methods for testing type relationships. aoqi@0: * aoqi@0: * @author Attila Szegedi aoqi@0: */ aoqi@0: public class TypeUtilities { aoqi@0: static final Class OBJECT_CLASS = Object.class; aoqi@0: aoqi@0: private TypeUtilities() { aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given two types represented by c1 and c2, returns a type that is their most specific common superclass or aoqi@0: * superinterface. aoqi@0: * aoqi@0: * @param c1 one type aoqi@0: * @param c2 another type aoqi@0: * @return their most common superclass or superinterface. If they have several unrelated superinterfaces as their aoqi@0: * most specific common type, or the types themselves are completely unrelated interfaces, {@link java.lang.Object} aoqi@0: * is returned. aoqi@0: */ aoqi@0: public static Class getMostSpecificCommonType(Class c1, Class c2) { aoqi@0: if(c1 == c2) { aoqi@0: return c1; aoqi@0: } aoqi@0: Class c3 = c2; aoqi@0: if(c3.isPrimitive()) { aoqi@0: if(c3 == Byte.TYPE) aoqi@0: c3 = Byte.class; aoqi@0: else if(c3 == Short.TYPE) aoqi@0: c3 = Short.class; aoqi@0: else if(c3 == Character.TYPE) aoqi@0: c3 = Character.class; aoqi@0: else if(c3 == Integer.TYPE) aoqi@0: c3 = Integer.class; aoqi@0: else if(c3 == Float.TYPE) aoqi@0: c3 = Float.class; aoqi@0: else if(c3 == Long.TYPE) aoqi@0: c3 = Long.class; aoqi@0: else if(c3 == Double.TYPE) aoqi@0: c3 = Double.class; aoqi@0: } aoqi@0: Set> a1 = getAssignables(c1, c3); aoqi@0: Set> a2 = getAssignables(c3, c1); aoqi@0: a1.retainAll(a2); aoqi@0: if(a1.isEmpty()) { aoqi@0: // Can happen when at least one of the arguments is an interface, aoqi@0: // as they don't have Object at the root of their hierarchy. aoqi@0: return Object.class; aoqi@0: } aoqi@0: // Gather maximally specific elements. Yes, there can be more than one aoqi@0: // thank to interfaces. I.e., if you call this method for String.class aoqi@0: // and Number.class, you'll have Comparable, Serializable, and Object aoqi@0: // as maximal elements. aoqi@0: List> max = new ArrayList<>(); aoqi@0: outer: for(Class clazz: a1) { aoqi@0: for(Iterator> maxiter = max.iterator(); maxiter.hasNext();) { aoqi@0: Class maxClazz = maxiter.next(); aoqi@0: if(isSubtype(maxClazz, clazz)) { aoqi@0: // It can't be maximal, if there's already a more specific aoqi@0: // maximal than it. aoqi@0: continue outer; aoqi@0: } aoqi@0: if(isSubtype(clazz, maxClazz)) { aoqi@0: // If it's more specific than a currently maximal element, aoqi@0: // that currently maximal is no longer a maximal. aoqi@0: maxiter.remove(); aoqi@0: } aoqi@0: } aoqi@0: // If we get here, no current maximal is more specific than the aoqi@0: // current class, so it is considered maximal as well aoqi@0: max.add(clazz); aoqi@0: } aoqi@0: if(max.size() > 1) { aoqi@0: return OBJECT_CLASS; aoqi@0: } aoqi@0: return max.get(0); aoqi@0: } aoqi@0: aoqi@0: private static Set> getAssignables(Class c1, Class c2) { aoqi@0: Set> s = new HashSet<>(); aoqi@0: collectAssignables(c1, c2, s); aoqi@0: return s; aoqi@0: } aoqi@0: aoqi@0: private static void collectAssignables(Class c1, Class c2, Set> s) { aoqi@0: if(c1.isAssignableFrom(c2)) { aoqi@0: s.add(c1); aoqi@0: } aoqi@0: Class sc = c1.getSuperclass(); aoqi@0: if(sc != null) { aoqi@0: collectAssignables(sc, c2, s); aoqi@0: } aoqi@0: Class[] itf = c1.getInterfaces(); aoqi@0: for(int i = 0; i < itf.length; ++i) { aoqi@0: collectAssignables(itf[i], c2, s); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: private static final Map, Class> WRAPPER_TYPES = createWrapperTypes(); aoqi@0: private static final Map, Class> PRIMITIVE_TYPES = invertMap(WRAPPER_TYPES); aoqi@0: private static final Map> PRIMITIVE_TYPES_BY_NAME = createClassNameMapping(WRAPPER_TYPES.keySet()); aoqi@0: aoqi@0: private static Map, Class> createWrapperTypes() { aoqi@0: final Map, Class> wrapperTypes = new IdentityHashMap<>(8); aoqi@0: wrapperTypes.put(Boolean.TYPE, Boolean.class); aoqi@0: wrapperTypes.put(Byte.TYPE, Byte.class); aoqi@0: wrapperTypes.put(Character.TYPE, Character.class); aoqi@0: wrapperTypes.put(Short.TYPE, Short.class); aoqi@0: wrapperTypes.put(Integer.TYPE, Integer.class); aoqi@0: wrapperTypes.put(Long.TYPE, Long.class); aoqi@0: wrapperTypes.put(Float.TYPE, Float.class); aoqi@0: wrapperTypes.put(Double.TYPE, Double.class); aoqi@0: return Collections.unmodifiableMap(wrapperTypes); aoqi@0: } aoqi@0: aoqi@0: private static Map> createClassNameMapping(Collection> classes) { aoqi@0: final Map> map = new HashMap<>(); aoqi@0: for(Class clazz: classes) { aoqi@0: map.put(clazz.getName(), clazz); aoqi@0: } aoqi@0: return map; aoqi@0: } aoqi@0: aoqi@0: private static Map invertMap(Map map) { aoqi@0: final Map inverted = new IdentityHashMap<>(map.size()); aoqi@0: for(Map.Entry entry: map.entrySet()) { aoqi@0: inverted.put(entry.getValue(), entry.getKey()); aoqi@0: } aoqi@0: return Collections.unmodifiableMap(inverted); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Determines whether one type can be converted to another type using a method invocation conversion, as per JLS 5.3 aoqi@0: * "Method Invocation Conversion". This is basically all conversions allowed by subtyping (see aoqi@0: * {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening aoqi@0: * reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion. aoqi@0: * aoqi@0: * @param callSiteType the parameter type at the call site aoqi@0: * @param methodType the parameter type in the method declaration aoqi@0: * @return true if callSiteType is method invocation convertible to the methodType. aoqi@0: */ aoqi@0: public static boolean isMethodInvocationConvertible(Class callSiteType, Class methodType) { aoqi@0: if(methodType.isAssignableFrom(callSiteType)) { aoqi@0: return true; aoqi@0: } aoqi@0: if(callSiteType.isPrimitive()) { aoqi@0: if(methodType.isPrimitive()) { aoqi@0: return isProperPrimitiveSubtype(callSiteType, methodType); aoqi@0: } aoqi@0: // Boxing + widening reference conversion aoqi@0: return methodType.isAssignableFrom(WRAPPER_TYPES.get(callSiteType)); aoqi@0: } aoqi@0: if(methodType.isPrimitive()) { aoqi@0: final Class unboxedCallSiteType = PRIMITIVE_TYPES.get(callSiteType); aoqi@0: return unboxedCallSiteType != null aoqi@0: && (unboxedCallSiteType == methodType || isProperPrimitiveSubtype(unboxedCallSiteType, methodType)); aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between aoqi@0: * any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as aoqi@0: * well as between any primitive type and any reference type that can hold a boxed primitive. aoqi@0: * aoqi@0: * @param callSiteType the parameter type at the call site aoqi@0: * @param methodType the parameter type in the method declaration aoqi@0: * @return true if callSiteType is potentially convertible to the methodType. aoqi@0: */ aoqi@0: public static boolean isPotentiallyConvertible(Class callSiteType, Class methodType) { aoqi@0: // Widening or narrowing reference conversion aoqi@0: if(methodType.isAssignableFrom(callSiteType) || callSiteType.isAssignableFrom(methodType)) { aoqi@0: return true; aoqi@0: } aoqi@0: if(callSiteType.isPrimitive()) { aoqi@0: // Allow any conversion among primitives, as well as from any aoqi@0: // primitive to any type that can receive a boxed primitive. aoqi@0: // TODO: narrow this a bit, i.e. allow, say, boolean to Character? aoqi@0: // MethodHandles.convertArguments() allows it, so we might need to aoqi@0: // too. aoqi@0: return methodType.isPrimitive() || isAssignableFromBoxedPrimitive(methodType); aoqi@0: } aoqi@0: if(methodType.isPrimitive()) { aoqi@0: // Allow conversion from any reference type that can contain a aoqi@0: // boxed primitive to any primitive. aoqi@0: // TODO: narrow this a bit too? aoqi@0: return isAssignableFromBoxedPrimitive(callSiteType); aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict aoqi@0: * or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows aoqi@0: * identity conversion (JLS 5.1.1), widening primitive conversion (JLS 5.1.2) and widening reference conversion (JLS aoqi@0: * 5.1.5). aoqi@0: * aoqi@0: * @param subType the supposed subtype aoqi@0: * @param superType the supposed supertype of the subtype aoqi@0: * @return true if subType can be converted by identity conversion, widening primitive conversion, or widening aoqi@0: * reference conversion to superType. aoqi@0: */ aoqi@0: public static boolean isSubtype(Class subType, Class superType) { aoqi@0: // Covers both JLS 4.10.2 "Subtyping among Class and Interface Types" aoqi@0: // and JLS 4.10.3 "Subtyping among Array Types", as well as primitive aoqi@0: // type identity. aoqi@0: if(superType.isAssignableFrom(subType)) { aoqi@0: return true; aoqi@0: } aoqi@0: // JLS 4.10.1 "Subtyping among Primitive Types". Note we don't test for aoqi@0: // identity, as identical types were taken care of in the aoqi@0: // isAssignableFrom test. As per 4.10.1, the supertype relation is as aoqi@0: // follows: aoqi@0: // double > float aoqi@0: // float > long aoqi@0: // long > int aoqi@0: // int > short aoqi@0: // int > char aoqi@0: // short > byte aoqi@0: if(superType.isPrimitive() && subType.isPrimitive()) { aoqi@0: return isProperPrimitiveSubtype(subType, superType); aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns true if a supposed primitive subtype is a proper subtype ( meaning, subtype and not identical) of the aoqi@0: * supposed primitive supertype aoqi@0: * aoqi@0: * @param subType the supposed subtype aoqi@0: * @param superType the supposed supertype aoqi@0: * @return true if subType is a proper (not identical to) primitive subtype of the superType aoqi@0: */ aoqi@0: private static boolean isProperPrimitiveSubtype(Class subType, Class superType) { aoqi@0: if(superType == boolean.class || subType == boolean.class) { aoqi@0: return false; aoqi@0: } aoqi@0: if(subType == byte.class) { aoqi@0: return superType != char.class; aoqi@0: } aoqi@0: if(subType == char.class) { aoqi@0: return superType != short.class && superType != byte.class; aoqi@0: } aoqi@0: if(subType == short.class) { aoqi@0: return superType != char.class && superType != byte.class; aoqi@0: } aoqi@0: if(subType == int.class) { aoqi@0: return superType == long.class || superType == float.class || superType == double.class; aoqi@0: } aoqi@0: if(subType == long.class) { aoqi@0: return superType == float.class || superType == double.class; aoqi@0: } aoqi@0: if(subType == float.class) { aoqi@0: return superType == double.class; aoqi@0: } aoqi@0: return false; aoqi@0: } aoqi@0: aoqi@0: private static final Map, Class> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes(); aoqi@0: aoqi@0: private static Map, Class> createWrapperToPrimitiveTypes() { aoqi@0: final Map, Class> classes = new IdentityHashMap<>(); aoqi@0: classes.put(Void.class, Void.TYPE); aoqi@0: classes.put(Boolean.class, Boolean.TYPE); aoqi@0: classes.put(Byte.class, Byte.TYPE); aoqi@0: classes.put(Character.class, Character.TYPE); aoqi@0: classes.put(Short.class, Short.TYPE); aoqi@0: classes.put(Integer.class, Integer.TYPE); aoqi@0: classes.put(Long.class, Long.TYPE); aoqi@0: classes.put(Float.class, Float.TYPE); aoqi@0: classes.put(Double.class, Double.TYPE); aoqi@0: return classes; aoqi@0: } aoqi@0: aoqi@0: private static final Set> PRIMITIVE_WRAPPER_TYPES = createPrimitiveWrapperTypes(); aoqi@0: aoqi@0: private static Set> createPrimitiveWrapperTypes() { aoqi@0: final Map, Class> classes = new IdentityHashMap<>(); aoqi@0: addClassHierarchy(classes, Boolean.class); aoqi@0: addClassHierarchy(classes, Byte.class); aoqi@0: addClassHierarchy(classes, Character.class); aoqi@0: addClassHierarchy(classes, Short.class); aoqi@0: addClassHierarchy(classes, Integer.class); aoqi@0: addClassHierarchy(classes, Long.class); aoqi@0: addClassHierarchy(classes, Float.class); aoqi@0: addClassHierarchy(classes, Double.class); aoqi@0: return classes.keySet(); aoqi@0: } aoqi@0: aoqi@0: private static void addClassHierarchy(Map, Class> map, Class clazz) { aoqi@0: if(clazz == null) { aoqi@0: return; aoqi@0: } aoqi@0: map.put(clazz, clazz); aoqi@0: addClassHierarchy(map, clazz.getSuperclass()); aoqi@0: for(Class itf: clazz.getInterfaces()) { aoqi@0: addClassHierarchy(map, itf); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Returns true if the class can be assigned from any boxed primitive. aoqi@0: * aoqi@0: * @param clazz the class aoqi@0: * @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any aoqi@0: * primitive wrapper class, or a superclass or superinterface of any primitive wrapper class. aoqi@0: */ aoqi@0: private static boolean isAssignableFromBoxedPrimitive(Class clazz) { aoqi@0: return PRIMITIVE_WRAPPER_TYPES.contains(clazz); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Given a name of a primitive type (except "void"), returns the class representing it. I.e. when invoked with aoqi@0: * "int", returns {@link Integer#TYPE}. aoqi@0: * @param name the name of the primitive type aoqi@0: * @return the class representing the primitive type, or null if the name does not correspond to a primitive type aoqi@0: * or is "void". aoqi@0: */ aoqi@0: public static Class getPrimitiveTypeByName(String name) { aoqi@0: return PRIMITIVE_TYPES_BY_NAME.get(name); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * When passed a class representing a wrapper for a primitive type, returns the class representing the corresponding aoqi@0: * primitive type. I.e. calling it with {@code Integer.class} will return {@code Integer.TYPE}. If passed a class aoqi@0: * that is not a wrapper for primitive type, returns null. aoqi@0: * @param wrapperType the class object representing a wrapper for a primitive type aoqi@0: * @return the class object representing the primitive type, or null if the passed class is not a primitive wrapper. aoqi@0: */ aoqi@0: public static Class getPrimitiveType(Class wrapperType) { aoqi@0: return WRAPPER_TO_PRIMITIVE_TYPES.get(wrapperType); aoqi@0: } aoqi@0: aoqi@0: aoqi@0: /** aoqi@0: * When passed a class representing a primitive type, returns the class representing the corresponding aoqi@0: * wrapper type. I.e. calling it with {@code int.class} will return {@code Integer.class}. If passed a class aoqi@0: * that is not a primitive type, returns null. aoqi@0: * @param primitiveType the class object representing a primitive type aoqi@0: * @return the class object representing the wrapper type, or null if the passed class is not a primitive. aoqi@0: */ aoqi@0: public static Class getWrapperType(Class primitiveType) { aoqi@0: return WRAPPER_TYPES.get(primitiveType); aoqi@0: } aoqi@0: }