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

Wed, 14 Jan 2015 15:54:18 +0100

author
attila
date
Wed, 14 Jan 2015 15:54:18 +0100
changeset 1183
6ed91931b5a7
parent 1090
99571b7922c0
child 1205
4112748288bb
permissions
-rw-r--r--

8068573: POJO setter using [] syntax throws an exception
Reviewed-by: lagergren, jlaskey

     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 (c1 == void.class || c2 == void.class) {
   122             return Object.class;
   123         } else if(isConvertibleWithoutLoss(c2, c1)) {
   124             return c1;
   125         } else if(isConvertibleWithoutLoss(c1, c2)) {
   126             return c2;
   127         } else if(c1.isPrimitive() && c2.isPrimitive()) {
   128             if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
   129                 // byte + char = int
   130                 return int.class;
   131             } else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
   132                 // short + char = int
   133                 return int.class;
   134             } else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
   135                 // int + float = double
   136                 return double.class;
   137             }
   138         }
   139         // For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
   140         return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
   141     }
   143     private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(final Class<?> c1, final Class<?> c2) {
   144         final Class<?> npc1 = c1.isPrimitive() ? getWrapperType(c1) : c1;
   145         final Class<?> npc2 = c2.isPrimitive() ? getWrapperType(c2) : c2;
   146         final Set<Class<?>> a1 = getAssignables(npc1, npc2);
   147         final Set<Class<?>> a2 = getAssignables(npc2, npc1);
   148         a1.retainAll(a2);
   149         if(a1.isEmpty()) {
   150             // Can happen when at least one of the arguments is an interface,
   151             // as they don't have Object at the root of their hierarchy.
   152             return Object.class;
   153         }
   154         // Gather maximally specific elements. Yes, there can be more than one
   155         // thank to interfaces. I.e., if you call this method for String.class
   156         // and Number.class, you'll have Comparable, Serializable, and Object
   157         // as maximal elements.
   158         final List<Class<?>> max = new ArrayList<>();
   159         outer: for(final Class<?> clazz: a1) {
   160             for(final Iterator<Class<?>> maxiter = max.iterator(); maxiter.hasNext();) {
   161                 final Class<?> maxClazz = maxiter.next();
   162                 if(isSubtype(maxClazz, clazz)) {
   163                     // It can't be maximal, if there's already a more specific
   164                     // maximal than it.
   165                     continue outer;
   166                 }
   167                 if(isSubtype(clazz, maxClazz)) {
   168                     // If it's more specific than a currently maximal element,
   169                     // that currently maximal is no longer a maximal.
   170                     maxiter.remove();
   171                 }
   172             }
   173             // If we get here, no current maximal is more specific than the
   174             // current class, so it is considered maximal as well
   175             max.add(clazz);
   176         }
   177         if(max.size() > 1) {
   178             return Object.class;
   179         }
   180         return max.get(0);
   181     }
   183     private static Set<Class<?>> getAssignables(final Class<?> c1, final Class<?> c2) {
   184         final Set<Class<?>> s = new HashSet<>();
   185         collectAssignables(c1, c2, s);
   186         return s;
   187     }
   189     private static void collectAssignables(final Class<?> c1, final Class<?> c2, final Set<Class<?>> s) {
   190         if(c1.isAssignableFrom(c2)) {
   191             s.add(c1);
   192         }
   193         final Class<?> sc = c1.getSuperclass();
   194         if(sc != null) {
   195             collectAssignables(sc, c2, s);
   196         }
   197         final Class<?>[] itf = c1.getInterfaces();
   198         for(int i = 0; i < itf.length; ++i) {
   199             collectAssignables(itf[i], c2, s);
   200         }
   201     }
   203     private static final Map<Class<?>, Class<?>> WRAPPER_TYPES = createWrapperTypes();
   204     private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPES = invertMap(WRAPPER_TYPES);
   205     private static final Map<String, Class<?>> PRIMITIVE_TYPES_BY_NAME = createClassNameMapping(WRAPPER_TYPES.keySet());
   207     private static Map<Class<?>, Class<?>> createWrapperTypes() {
   208         final Map<Class<?>, Class<?>> wrapperTypes = new IdentityHashMap<>(8);
   209         wrapperTypes.put(Boolean.TYPE, Boolean.class);
   210         wrapperTypes.put(Byte.TYPE, Byte.class);
   211         wrapperTypes.put(Character.TYPE, Character.class);
   212         wrapperTypes.put(Short.TYPE, Short.class);
   213         wrapperTypes.put(Integer.TYPE, Integer.class);
   214         wrapperTypes.put(Long.TYPE, Long.class);
   215         wrapperTypes.put(Float.TYPE, Float.class);
   216         wrapperTypes.put(Double.TYPE, Double.class);
   217         return Collections.unmodifiableMap(wrapperTypes);
   218     }
   220     private static Map<String, Class<?>> createClassNameMapping(final Collection<Class<?>> classes) {
   221         final Map<String, Class<?>> map = new HashMap<>();
   222         for(final Class<?> clazz: classes) {
   223             map.put(clazz.getName(), clazz);
   224         }
   225         return map;
   226     }
   228     private static <K, V> Map<V, K> invertMap(final Map<K, V> map) {
   229         final Map<V, K> inverted = new IdentityHashMap<>(map.size());
   230         for(final Map.Entry<K, V> entry: map.entrySet()) {
   231             inverted.put(entry.getValue(), entry.getKey());
   232         }
   233         return Collections.unmodifiableMap(inverted);
   234     }
   236     /**
   237      * Determines whether one type can be converted to another type using a method invocation conversion, as per JLS 5.3
   238      * "Method Invocation Conversion". This is basically all conversions allowed by subtyping (see
   239      * {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
   240      * reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
   241      *
   242      * @param sourceType the type being converted from (call site type for parameter types, method type for return types)
   243      * @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
   244      * @return true if source type is method invocation convertible to target type.
   245      */
   246     public static boolean isMethodInvocationConvertible(final Class<?> sourceType, final Class<?> targetType) {
   247         if(targetType.isAssignableFrom(sourceType)) {
   248             return true;
   249         }
   250         if(sourceType.isPrimitive()) {
   251             if(targetType.isPrimitive()) {
   252                 return isProperPrimitiveSubtype(sourceType, targetType);
   253             }
   254             // Boxing + widening reference conversion
   255             assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
   256             return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
   257         }
   258         if(targetType.isPrimitive()) {
   259             final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
   260             return unboxedCallSiteType != null
   261                     && (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
   262         }
   263         return false;
   264     }
   266     /**
   267      * Determines whether a type can be converted to another without losing any precision. As a special case,
   268      * void is considered convertible only to Object and void, while anything can be converted to void. This
   269      * is because a target type of void means we don't care about the value, so the conversion is always
   270      * permissible.
   271      *
   272      * @param sourceType the source type
   273      * @param targetType the target type
   274      * @return true if lossless conversion is possible
   275      */
   276     public static boolean isConvertibleWithoutLoss(final Class<?> sourceType, final Class<?> targetType) {
   277         if(targetType.isAssignableFrom(sourceType) || targetType == void.class) {
   278             return true;
   279         }
   280         if(sourceType.isPrimitive()) {
   281             if(sourceType == void.class) {
   282                 // Void should be losslessly representable by Object, either as null or as a custom value that
   283                 // can be set with DynamicLinkerFactory.setAutoConversionStrategy.
   284                 return targetType == Object.class;
   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