src/jdk/internal/dynalink/support/TypeUtilities.java

Mon, 03 Nov 2014 09:49:52 +0100

author
attila
date
Mon, 03 Nov 2014 09:49:52 +0100
changeset 1090
99571b7922c0
parent 963
e2497b11a021
child 1183
6ed91931b5a7
permissions
-rw-r--r--

8059443: NPE when unboxing return values
Reviewed-by: lagergren, sundar

     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.support;
    86 import java.util.ArrayList;
    87 import java.util.Collection;
    88 import java.util.Collections;
    89 import java.util.HashMap;
    90 import java.util.HashSet;
    91 import java.util.IdentityHashMap;
    92 import java.util.Iterator;
    93 import java.util.List;
    94 import java.util.Map;
    95 import java.util.Set;
    97 /**
    98  * Various static utility methods for testing type relationships.
    99  *
   100  * @author Attila Szegedi
   101  */
   102 public class TypeUtilities {
   103     static final Class<Object> OBJECT_CLASS = Object.class;
   105     private TypeUtilities() {
   106     }
   108     /**
   109      * Given two types represented by c1 and c2, returns a type that is their most specific common supertype for
   110      * purposes of lossless conversions.
   111      *
   112      * @param c1 one type
   113      * @param c2 another type
   114      * @return their most common superclass or superinterface for purposes of lossless conversions. If they have several
   115      * unrelated superinterfaces as their most specific common type, or the types themselves are completely
   116      * unrelated interfaces, {@link java.lang.Object} is returned.
   117      */
   118     public static Class<?> getCommonLosslessConversionType(final Class<?> c1, final Class<?> c2) {
   119         if(c1 == c2) {
   120             return c1;
   121         } else if(isConvertibleWithoutLoss(c2, c1)) {
   122             return c1;
   123         } else if(isConvertibleWithoutLoss(c1, c2)) {
   124             return c2;
   125         }
   126         if(c1 == void.class) {
   127             return c2;
   128         } else if(c2 == void.class) {
   129             return c1;
   130         }
   131         if(c1.isPrimitive() && c2.isPrimitive()) {
   132             if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
   133                 // byte + char = int
   134                 return int.class;
   135             } else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
   136                 // short + char = int
   137                 return int.class;
   138             } else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
   139                 // int + float = double
   140                 return double.class;
   141             }
   142         }
   143         // For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
   144         return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
   145     }
   147     private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(final Class<?> c1, final Class<?> c2) {
   148         final Class<?> npc1 = c1.isPrimitive() ? getWrapperType(c1) : c1;
   149         final Class<?> npc2 = c2.isPrimitive() ? getWrapperType(c2) : c2;
   150         final Set<Class<?>> a1 = getAssignables(npc1, npc2);
   151         final Set<Class<?>> a2 = getAssignables(npc2, npc1);
   152         a1.retainAll(a2);
   153         if(a1.isEmpty()) {
   154             // Can happen when at least one of the arguments is an interface,
   155             // as they don't have Object at the root of their hierarchy.
   156             return Object.class;
   157         }
   158         // Gather maximally specific elements. Yes, there can be more than one
   159         // thank to interfaces. I.e., if you call this method for String.class
   160         // and Number.class, you'll have Comparable, Serializable, and Object
   161         // as maximal elements.
   162         final List<Class<?>> max = new ArrayList<>();
   163         outer: for(final Class<?> clazz: a1) {
   164             for(final Iterator<Class<?>> maxiter = max.iterator(); maxiter.hasNext();) {
   165                 final Class<?> maxClazz = maxiter.next();
   166                 if(isSubtype(maxClazz, clazz)) {
   167                     // It can't be maximal, if there's already a more specific
   168                     // maximal than it.
   169                     continue outer;
   170                 }
   171                 if(isSubtype(clazz, maxClazz)) {
   172                     // If it's more specific than a currently maximal element,
   173                     // that currently maximal is no longer a maximal.
   174                     maxiter.remove();
   175                 }
   176             }
   177             // If we get here, no current maximal is more specific than the
   178             // current class, so it is considered maximal as well
   179             max.add(clazz);
   180         }
   181         if(max.size() > 1) {
   182             return Object.class;
   183         }
   184         return max.get(0);
   185     }
   187     private static Set<Class<?>> getAssignables(final Class<?> c1, final Class<?> c2) {
   188         final Set<Class<?>> s = new HashSet<>();
   189         collectAssignables(c1, c2, s);
   190         return s;
   191     }
   193     private static void collectAssignables(final Class<?> c1, final Class<?> c2, final Set<Class<?>> s) {
   194         if(c1.isAssignableFrom(c2)) {
   195             s.add(c1);
   196         }
   197         final Class<?> sc = c1.getSuperclass();
   198         if(sc != null) {
   199             collectAssignables(sc, c2, s);
   200         }
   201         final Class<?>[] itf = c1.getInterfaces();
   202         for(int i = 0; i < itf.length; ++i) {
   203             collectAssignables(itf[i], c2, s);
   204         }
   205     }
   207     private static final Map<Class<?>, Class<?>> WRAPPER_TYPES = createWrapperTypes();
   208     private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPES = invertMap(WRAPPER_TYPES);
   209     private static final Map<String, Class<?>> PRIMITIVE_TYPES_BY_NAME = createClassNameMapping(WRAPPER_TYPES.keySet());
   211     private static Map<Class<?>, Class<?>> createWrapperTypes() {
   212         final Map<Class<?>, Class<?>> wrapperTypes = new IdentityHashMap<>(8);
   213         wrapperTypes.put(Boolean.TYPE, Boolean.class);
   214         wrapperTypes.put(Byte.TYPE, Byte.class);
   215         wrapperTypes.put(Character.TYPE, Character.class);
   216         wrapperTypes.put(Short.TYPE, Short.class);
   217         wrapperTypes.put(Integer.TYPE, Integer.class);
   218         wrapperTypes.put(Long.TYPE, Long.class);
   219         wrapperTypes.put(Float.TYPE, Float.class);
   220         wrapperTypes.put(Double.TYPE, Double.class);
   221         return Collections.unmodifiableMap(wrapperTypes);
   222     }
   224     private static Map<String, Class<?>> createClassNameMapping(final Collection<Class<?>> classes) {
   225         final Map<String, Class<?>> map = new HashMap<>();
   226         for(final Class<?> clazz: classes) {
   227             map.put(clazz.getName(), clazz);
   228         }
   229         return map;
   230     }
   232     private static <K, V> Map<V, K> invertMap(final Map<K, V> map) {
   233         final Map<V, K> inverted = new IdentityHashMap<>(map.size());
   234         for(final Map.Entry<K, V> entry: map.entrySet()) {
   235             inverted.put(entry.getValue(), entry.getKey());
   236         }
   237         return Collections.unmodifiableMap(inverted);
   238     }
   240     /**
   241      * Determines whether one type can be converted to another type using a method invocation conversion, as per JLS 5.3
   242      * "Method Invocation Conversion". This is basically all conversions allowed by subtyping (see
   243      * {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
   244      * reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
   245      *
   246      * @param sourceType the type being converted from (call site type for parameter types, method type for return types)
   247      * @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
   248      * @return true if source type is method invocation convertible to target type.
   249      */
   250     public static boolean isMethodInvocationConvertible(final Class<?> sourceType, final Class<?> targetType) {
   251         if(targetType.isAssignableFrom(sourceType)) {
   252             return true;
   253         }
   254         if(sourceType.isPrimitive()) {
   255             if(targetType.isPrimitive()) {
   256                 return isProperPrimitiveSubtype(sourceType, targetType);
   257             }
   258             // Boxing + widening reference conversion
   259             assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
   260             return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
   261         }
   262         if(targetType.isPrimitive()) {
   263             final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
   264             return unboxedCallSiteType != null
   265                     && (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
   266         }
   267         return false;
   268     }
   270     /**
   271      * Determines whether a type can be converted to another without losing any
   272      * precision.
   273      *
   274      * @param sourceType the source type
   275      * @param targetType the target type
   276      * @return true if lossless conversion is possible
   277      */
   278     public static boolean isConvertibleWithoutLoss(final Class<?> sourceType, final Class<?> targetType) {
   279         if(targetType.isAssignableFrom(sourceType)) {
   280             return true;
   281         }
   282         if(sourceType.isPrimitive()) {
   283             if(sourceType == void.class) {
   284                 return false; // Void can't be losslessly represented by any type
   285             }
   286             if(targetType.isPrimitive()) {
   287                 return isProperPrimitiveLosslessSubtype(sourceType, targetType);
   288             }
   289             // Boxing + widening reference conversion
   290             assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
   291             return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
   292         }
   293         // Can't convert from any non-primitive type to any primitive type without data loss because of null.
   294         // Also, can't convert non-assignable reference types.
   295         return false;
   296     }
   298     /**
   299      * Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between
   300      * any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as
   301      * well as between any primitive type and any reference type that can hold a boxed primitive.
   302      *
   303      * @param callSiteType the parameter type at the call site
   304      * @param methodType the parameter type in the method declaration
   305      * @return true if callSiteType is potentially convertible to the methodType.
   306      */
   307     public static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {
   308         // Widening or narrowing reference conversion
   309         if(areAssignable(callSiteType, methodType)) {
   310             return true;
   311         }
   312         if(callSiteType.isPrimitive()) {
   313             // Allow any conversion among primitives, as well as from any
   314             // primitive to any type that can receive a boxed primitive.
   315             // TODO: narrow this a bit, i.e. allow, say, boolean to Character?
   316             // MethodHandles.convertArguments() allows it, so we might need to
   317             // too.
   318             return methodType.isPrimitive() || isAssignableFromBoxedPrimitive(methodType);
   319         }
   320         if(methodType.isPrimitive()) {
   321             // Allow conversion from any reference type that can contain a
   322             // boxed primitive to any primitive.
   323             // TODO: narrow this a bit too?
   324             return isAssignableFromBoxedPrimitive(callSiteType);
   325         }
   326         return false;
   327     }
   329     /**
   330      * Returns true if either of the types is assignable from the other.
   331      * @param c1 one of the types
   332      * @param c2 another one of the types
   333      * @return true if either c1 is assignable from c2 or c2 is assignable from c1.
   334      */
   335     public static boolean areAssignable(final Class<?> c1, final Class<?> c2) {
   336         return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
   337     }
   339     /**
   340      * Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict
   341      * or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows
   342      * identity conversion (JLS 5.1.1), widening primitive conversion (JLS 5.1.2) and widening reference conversion (JLS
   343      * 5.1.5).
   344      *
   345      * @param subType the supposed subtype
   346      * @param superType the supposed supertype of the subtype
   347      * @return true if subType can be converted by identity conversion, widening primitive conversion, or widening
   348      * reference conversion to superType.
   349      */
   350     public static boolean isSubtype(final Class<?> subType, final Class<?> superType) {
   351         // Covers both JLS 4.10.2 "Subtyping among Class and Interface Types"
   352         // and JLS 4.10.3 "Subtyping among Array Types", as well as primitive
   353         // type identity.
   354         if(superType.isAssignableFrom(subType)) {
   355             return true;
   356         }
   357         // JLS 4.10.1 "Subtyping among Primitive Types". Note we don't test for
   358         // identity, as identical types were taken care of in the
   359         // isAssignableFrom test. As per 4.10.1, the supertype relation is as
   360         // follows:
   361         // double > float
   362         // float > long
   363         // long > int
   364         // int > short
   365         // int > char
   366         // short > byte
   367         if(superType.isPrimitive() && subType.isPrimitive()) {
   368             return isProperPrimitiveSubtype(subType, superType);
   369         }
   370         return false;
   371     }
   373     /**
   374      * Returns true if a supposed primitive subtype is a proper subtype ( meaning, subtype and not identical) of the
   375      * supposed primitive supertype
   376      *
   377      * @param subType the supposed subtype
   378      * @param superType the supposed supertype
   379      * @return true if subType is a proper (not identical to) primitive subtype of the superType
   380      */
   381     private static boolean isProperPrimitiveSubtype(final Class<?> subType, final Class<?> superType) {
   382         if(superType == boolean.class || subType == boolean.class) {
   383             return false;
   384         }
   385         if(subType == byte.class) {
   386             return superType != char.class;
   387         }
   388         if(subType == char.class) {
   389             return superType != short.class && superType != byte.class;
   390         }
   391         if(subType == short.class) {
   392             return superType != char.class && superType != byte.class;
   393         }
   394         if(subType == int.class) {
   395             return superType == long.class || superType == float.class || superType == double.class;
   396         }
   397         if(subType == long.class) {
   398             return superType == float.class || superType == double.class;
   399         }
   400         if(subType == float.class) {
   401             return superType == double.class;
   402         }
   403         return false;
   404     }
   406     /**
   407      * Similar to {@link #isProperPrimitiveSubtype(Class, Class)}, except it disallows conversions from int and long to
   408      * float, and from long to double, as those can lose precision. It also disallows conversion from and to char and
   409      * anything else (similar to boolean) as char is not meant to be an arithmetic type.
   410      * @param subType the supposed subtype
   411      * @param superType the supposed supertype
   412      * @return true if subType is a proper (not identical to) primitive subtype of the superType that can be represented
   413      * by the supertype without no precision loss.
   414      */
   415     private static boolean isProperPrimitiveLosslessSubtype(final Class<?> subType, final Class<?> superType) {
   416         if(superType == boolean.class || subType == boolean.class) {
   417             return false;
   418         }
   419         if(superType == char.class || subType == char.class) {
   420             return false;
   421         }
   422         if(subType == byte.class) {
   423             return true;
   424         }
   425         if(subType == short.class) {
   426             return superType != byte.class;
   427         }
   428         if(subType == int.class) {
   429             return superType == long.class || superType == double.class;
   430         }
   431         if(subType == float.class) {
   432             return superType == double.class;
   433         }
   434         return false;
   435     }
   437     private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
   439     private static Map<Class<?>, Class<?>> createWrapperToPrimitiveTypes() {
   440         final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();
   441         classes.put(Void.class, Void.TYPE);
   442         classes.put(Boolean.class, Boolean.TYPE);
   443         classes.put(Byte.class, Byte.TYPE);
   444         classes.put(Character.class, Character.TYPE);
   445         classes.put(Short.class, Short.TYPE);
   446         classes.put(Integer.class, Integer.TYPE);
   447         classes.put(Long.class, Long.TYPE);
   448         classes.put(Float.class, Float.TYPE);
   449         classes.put(Double.class, Double.TYPE);
   450         return classes;
   451     }
   453     private static final Set<Class<?>> PRIMITIVE_WRAPPER_TYPES = createPrimitiveWrapperTypes();
   455     private static Set<Class<?>> createPrimitiveWrapperTypes() {
   456         final Map<Class<?>, Class<?>> classes = new IdentityHashMap<>();
   457         addClassHierarchy(classes, Boolean.class);
   458         addClassHierarchy(classes, Byte.class);
   459         addClassHierarchy(classes, Character.class);
   460         addClassHierarchy(classes, Short.class);
   461         addClassHierarchy(classes, Integer.class);
   462         addClassHierarchy(classes, Long.class);
   463         addClassHierarchy(classes, Float.class);
   464         addClassHierarchy(classes, Double.class);
   465         return classes.keySet();
   466     }
   468     private static void addClassHierarchy(final Map<Class<?>, Class<?>> map, final Class<?> clazz) {
   469         if(clazz == null) {
   470             return;
   471         }
   472         map.put(clazz, clazz);
   473         addClassHierarchy(map, clazz.getSuperclass());
   474         for(final Class<?> itf: clazz.getInterfaces()) {
   475             addClassHierarchy(map, itf);
   476         }
   477     }
   479     /**
   480      * Returns true if the class can be assigned from any boxed primitive.
   481      *
   482      * @param clazz the class
   483      * @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any
   484      * primitive wrapper class, or a superclass or superinterface of any primitive wrapper class.
   485      */
   486     private static boolean isAssignableFromBoxedPrimitive(final Class<?> clazz) {
   487         return PRIMITIVE_WRAPPER_TYPES.contains(clazz);
   488     }
   490     /**
   491      * Given a name of a primitive type (except "void"), returns the class representing it. I.e. when invoked with
   492      * "int", returns {@link Integer#TYPE}.
   493      * @param name the name of the primitive type
   494      * @return the class representing the primitive type, or null if the name does not correspond to a primitive type
   495      * or is "void".
   496      */
   497     public static Class<?> getPrimitiveTypeByName(final String name) {
   498         return PRIMITIVE_TYPES_BY_NAME.get(name);
   499     }
   501     /**
   502      * When passed a class representing a wrapper for a primitive type, returns the class representing the corresponding
   503      * primitive type. I.e. calling it with {@code Integer.class} will return {@code Integer.TYPE}. If passed a class
   504      * that is not a wrapper for primitive type, returns null.
   505      * @param wrapperType the class object representing a wrapper for a primitive type
   506      * @return the class object representing the primitive type, or null if the passed class is not a primitive wrapper.
   507      */
   508     public static Class<?> getPrimitiveType(final Class<?> wrapperType) {
   509         return WRAPPER_TO_PRIMITIVE_TYPES.get(wrapperType);
   510     }
   513     /**
   514      * When passed a class representing a primitive type, returns the class representing the corresponding
   515      * wrapper type. I.e. calling it with {@code int.class} will return {@code Integer.class}. If passed a class
   516      * that is not a primitive type, returns null.
   517      * @param primitiveType the class object representing a primitive type
   518      * @return the class object representing the wrapper type, or null if the passed class is not a primitive.
   519      */
   520     public static Class<?> getWrapperType(final Class<?> primitiveType) {
   521         return WRAPPER_TYPES.get(primitiveType);
   522     }
   524     /**
   525      * Returns true if the passed type is a wrapper for a primitive type.
   526      * @param type the examined type
   527      * @return true if the passed type is a wrapper for a primitive type.
   528      */
   529     public static boolean isWrapperType(final Class<?> type) {
   530         return PRIMITIVE_TYPES.containsKey(type);
   531     }
   532 }

mercurial