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.linker.support; 85 86import java.util.Collection; 87import java.util.Collections; 88import java.util.HashMap; 89import java.util.IdentityHashMap; 90import java.util.Map; 91import jdk.dynalink.DynamicLinkerFactory; 92import jdk.dynalink.linker.MethodTypeConversionStrategy; 93 94/** 95 * Various static utility methods for working with Java types. 96 */ 97public final class TypeUtilities { 98 static final Class<Object> OBJECT_CLASS = Object.class; 99 100 private TypeUtilities() { 101 } 102 103 private static final Map<Class<?>, Class<?>> WRAPPER_TYPES = createWrapperTypes(); 104 private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPES = invertMap(WRAPPER_TYPES); 105 private static final Map<String, Class<?>> PRIMITIVE_TYPES_BY_NAME = createClassNameMapping(WRAPPER_TYPES.keySet()); 106 107 private static Map<Class<?>, Class<?>> createWrapperTypes() { 108 final Map<Class<?>, Class<?>> wrapperTypes = new IdentityHashMap<>(8); 109 wrapperTypes.put(Void.TYPE, Void.class); 110 wrapperTypes.put(Boolean.TYPE, Boolean.class); 111 wrapperTypes.put(Byte.TYPE, Byte.class); 112 wrapperTypes.put(Character.TYPE, Character.class); 113 wrapperTypes.put(Short.TYPE, Short.class); 114 wrapperTypes.put(Integer.TYPE, Integer.class); 115 wrapperTypes.put(Long.TYPE, Long.class); 116 wrapperTypes.put(Float.TYPE, Float.class); 117 wrapperTypes.put(Double.TYPE, Double.class); 118 return Collections.unmodifiableMap(wrapperTypes); 119 } 120 121 private static Map<String, Class<?>> createClassNameMapping(final Collection<Class<?>> classes) { 122 final Map<String, Class<?>> map = new HashMap<>(); 123 for(final Class<?> clazz: classes) { 124 map.put(clazz.getName(), clazz); 125 } 126 return map; 127 } 128 129 private static <K, V> Map<V, K> invertMap(final Map<K, V> map) { 130 final Map<V, K> inverted = new IdentityHashMap<>(map.size()); 131 for(final Map.Entry<K, V> entry: map.entrySet()) { 132 inverted.put(entry.getValue(), entry.getKey()); 133 } 134 return Collections.unmodifiableMap(inverted); 135 } 136 137 /** 138 * Determines whether one type can be converted to another type using a method invocation conversion, as per JLS 5.3 139 * "Method Invocation Conversion". This is basically all conversions allowed by subtyping (see 140 * {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening 141 * reference conversion, and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion. 142 * 143 * @param sourceType the type being converted from (call site type for parameter types, method type for return types) 144 * @param targetType the parameter type being converted to (method type for parameter types, call site type for return types) 145 * @return true if source type is method invocation convertible to target type. 146 */ 147 public static boolean isMethodInvocationConvertible(final Class<?> sourceType, final Class<?> targetType) { 148 if(targetType.isAssignableFrom(sourceType)) { 149 return true; 150 } 151 if(sourceType.isPrimitive()) { 152 if(targetType.isPrimitive()) { 153 return isProperPrimitiveSubtype(sourceType, targetType); 154 } 155 return isBoxingAndWideningReferenceConversion(sourceType, targetType); 156 } 157 if(targetType.isPrimitive()) { 158 final Class<?> unboxedCallSiteType = getPrimitiveType(sourceType); 159 return unboxedCallSiteType != null 160 && (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType)); 161 } 162 return false; 163 } 164 165 private static boolean isBoxingAndWideningReferenceConversion(final Class<?> sourceType, final Class<?> targetType) { 166 final Class<?> wrapperType = getWrapperType(sourceType); 167 assert wrapperType != null : sourceType.getName(); 168 return targetType.isAssignableFrom(wrapperType); 169 } 170 171 /** 172 * Determines whether a type can be converted to another without losing any 173 * precision. As a special case, void is considered convertible only to void 174 * and {@link Object} (either as {@code null} or as a custom value set in 175 * {@link DynamicLinkerFactory#setAutoConversionStrategy(MethodTypeConversionStrategy)}). 176 * Somewhat unintuitively, we consider anything to be convertible to void 177 * even though converting to void causes the ultimate loss of data. On the 178 * other hand, conversion to void essentially means that the value is of no 179 * interest and should be discarded, thus there's no expectation of 180 * preserving any precision. 181 * 182 * @param sourceType the source type 183 * @param targetType the target type 184 * @return true if lossless conversion is possible 185 */ 186 public static boolean isConvertibleWithoutLoss(final Class<?> sourceType, final Class<?> targetType) { 187 if(targetType.isAssignableFrom(sourceType) || targetType == void.class) { 188 return true; 189 } 190 if(sourceType.isPrimitive()) { 191 if(sourceType == void.class) { 192 // Void should be losslessly representable by Object, either as null or as a custom value that 193 // can be set with DynamicLinkerFactory.setAutoConversionStrategy. 194 return targetType == Object.class; 195 } 196 if(targetType.isPrimitive()) { 197 return isProperPrimitiveLosslessSubtype(sourceType, targetType); 198 } 199 return isBoxingAndWideningReferenceConversion(sourceType, targetType); 200 } 201 // Can't convert from any non-primitive type to any primitive type without data loss because of null. 202 // Also, can't convert non-assignable reference types. 203 return false; 204 } 205 206 /** 207 * Determines whether one type is a subtype of another type, as per JLS 208 * 4.10 "Subtyping". Note: this is not strict or proper subtype, therefore 209 * true is also returned for identical types; to be completely precise, it 210 * allows identity conversion (JLS 5.1.1), widening primitive conversion 211 * (JLS 5.1.2) and widening reference conversion (JLS 5.1.5). 212 * 213 * @param subType the supposed subtype 214 * @param superType the supposed supertype of the subtype 215 * @return true if subType can be converted by identity conversion, widening primitive conversion, or widening 216 * reference conversion to superType. 217 */ 218 public static boolean isSubtype(final Class<?> subType, final Class<?> superType) { 219 // Covers both JLS 4.10.2 "Subtyping among Class and Interface Types" 220 // and JLS 4.10.3 "Subtyping among Array Types", as well as primitive 221 // type identity. 222 if(superType.isAssignableFrom(subType)) { 223 return true; 224 } 225 // JLS 4.10.1 "Subtyping among Primitive Types". Note we don't test for 226 // identity, as identical types were taken care of in the 227 // isAssignableFrom test. As per 4.10.1, the supertype relation is as 228 // follows: 229 // double > float 230 // float > long 231 // long > int 232 // int > short 233 // int > char 234 // short > byte 235 if(superType.isPrimitive() && subType.isPrimitive()) { 236 return isProperPrimitiveSubtype(subType, superType); 237 } 238 return false; 239 } 240 241 /** 242 * Returns true if a supposed primitive subtype is a proper subtype ( meaning, subtype and not identical) of the 243 * supposed primitive supertype 244 * 245 * @param subType the supposed subtype 246 * @param superType the supposed supertype 247 * @return true if subType is a proper (not identical to) primitive subtype of the superType 248 */ 249 private static boolean isProperPrimitiveSubtype(final Class<?> subType, final Class<?> superType) { 250 if(superType == boolean.class || subType == boolean.class) { 251 return false; 252 } 253 if(subType == byte.class) { 254 return superType != char.class; 255 } 256 if(subType == char.class) { 257 return superType != short.class && superType != byte.class; 258 } 259 if(subType == short.class) { 260 return superType != char.class && superType != byte.class; 261 } 262 if(subType == int.class) { 263 return superType == long.class || superType == float.class || superType == double.class; 264 } 265 if(subType == long.class) { 266 return superType == float.class || superType == double.class; 267 } 268 if(subType == float.class) { 269 return superType == double.class; 270 } 271 return false; 272 } 273 274 /** 275 * Similar to {@link #isProperPrimitiveSubtype(Class, Class)}, except it disallows conversions from int and long to 276 * float, and from long to double, as those can lose precision. It also disallows conversion from and to char and 277 * anything else (similar to boolean) as char is not meant to be an arithmetic type. 278 * @param subType the supposed subtype 279 * @param superType the supposed supertype 280 * @return true if subType is a proper (not identical to) primitive subtype of the superType that can be represented 281 * by the supertype without no precision loss. 282 */ 283 private static boolean isProperPrimitiveLosslessSubtype(final Class<?> subType, final Class<?> superType) { 284 if(superType == boolean.class || subType == boolean.class) { 285 return false; 286 } 287 if(superType == char.class || subType == char.class) { 288 return false; 289 } 290 if(subType == byte.class) { 291 return true; 292 } 293 if(subType == short.class) { 294 return superType != byte.class; 295 } 296 if(subType == int.class) { 297 return superType == long.class || superType == double.class; 298 } 299 if(subType == float.class) { 300 return superType == double.class; 301 } 302 return false; 303 } 304 305 /** 306 * Given a name of a primitive type returns the class representing it. I.e. 307 * when invoked with "int", returns {@link Integer#TYPE}. 308 * @param name the name of the primitive type 309 * @return the class representing the primitive type, or null if the name 310 * does not correspond to a primitive type. 311 */ 312 public static Class<?> getPrimitiveTypeByName(final String name) { 313 return PRIMITIVE_TYPES_BY_NAME.get(name); 314 } 315 316 /** 317 * When passed a class representing a wrapper for a primitive type, returns 318 * the class representing the corresponding primitive type. I.e. calling it 319 * with {@code Integer.class} will return {@code Integer.TYPE}. If passed a 320 * class that is not a wrapper for primitive type, returns null. 321 * @param wrapperType the class object representing a wrapper for a 322 * primitive type. 323 * @return the class object representing the primitive type, or null if the 324 * passed class is not a primitive wrapper. 325 */ 326 public static Class<?> getPrimitiveType(final Class<?> wrapperType) { 327 return PRIMITIVE_TYPES.get(wrapperType); 328 } 329 330 /** 331 * When passed a class representing a primitive type, returns the class representing the corresponding 332 * wrapper type. I.e. calling it with {@code int.class} will return {@code Integer.class}. If passed a class 333 * that is not a primitive type, returns null. 334 * @param primitiveType the class object representing a primitive type 335 * @return the class object representing the wrapper type, or null if the passed class is not a primitive. 336 */ 337 public static Class<?> getWrapperType(final Class<?> primitiveType) { 338 return WRAPPER_TYPES.get(primitiveType); 339 } 340 341 /** 342 * Returns true if the passed type is a wrapper for a primitive type. 343 * @param type the examined type 344 * @return true if the passed type is a wrapper for a primitive type. 345 */ 346 public static boolean isWrapperType(final Class<?> type) { 347 return PRIMITIVE_TYPES.containsKey(type); 348 } 349} 350