1 /* 2 * Copyright (c) 2011, 2021, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package vm.mlvm.meth.share; 25 26 import java.util.HashMap; 27 import java.util.LinkedList; 28 import java.util.List; 29 import java.util.Map; 30 import java.util.Map.Entry; 31 32 import nsk.share.test.TestUtils; 33 import vm.mlvm.share.Env; 34 35 public class TestTypes { 36 37 public static final Class<?>[] TYPES = { 38 // void.class 39 boolean.class, byte.class, char.class, short.class, int.class, long.class, 40 float.class, double.class, Object.class, String.class 41 }; 42 43 public static final Map<Class<?>, Class<?>> BOX_MAP = new HashMap<Class<?>, Class<?>>(); 44 public static final Map<Class<?>, Class<?>> UNBOX_MAP = new HashMap<Class<?>, Class<?>>(); 45 static { BOX_MAP.put(boolean.class, Boolean.class)46 BOX_MAP.put(boolean.class, Boolean.class); BOX_MAP.put(byte.class, Byte.class)47 BOX_MAP.put(byte.class, Byte.class); BOX_MAP.put(char.class, Character.class)48 BOX_MAP.put(char.class, Character.class); BOX_MAP.put(short.class, Short.class)49 BOX_MAP.put(short.class, Short.class); BOX_MAP.put(int.class, Integer.class)50 BOX_MAP.put(int.class, Integer.class); BOX_MAP.put(long.class, Long.class)51 BOX_MAP.put(long.class, Long.class); BOX_MAP.put(float.class, Float.class)52 BOX_MAP.put(float.class, Float.class); BOX_MAP.put(double.class, Double.class)53 BOX_MAP.put(double.class, Double.class); 54 55 for ( Entry<Class<?>, Class<?>> e : BOX_MAP.entrySet() ) { e.getValue()56 UNBOX_MAP.put(e.getValue(), e.getKey()); 57 } 58 } 59 60 public static final Class<?>[] PRIMITIVE_WIDENING_HIERARCHY = { 61 byte.class, short.class, char.class, int.class, long.class, float.class, double.class 62 }; 63 isBoxedType(Class<?> type)64 public static boolean isBoxedType(Class<?> type) { 65 return BOX_MAP.values().contains(type); 66 } 67 addPrimitiveAndBoxed(List<Class<?>> list, Class<?> type)68 private static void addPrimitiveAndBoxed(List<Class<?>> list, Class<?> type) { 69 list.add(type); 70 list.add(BOX_MAP.get(type)); 71 } 72 73 /** 74 * WPC = JLS 5.1.2 Widening Primitive Conversions 75 * @param type 76 * @return 77 */ addWPCAssignableTypesFor(List<Class<?>> result, Class<?> type)78 private static void addWPCAssignableTypesFor(List<Class<?>> result, Class<?> type) { 79 if ( type.equals(short.class) ) { 80 addPrimitiveAndBoxed(result, byte.class); 81 } 82 83 if ( type.equals(int.class) || type.equals(long.class) || type.equals(float.class) || type.equals(double.class) ) { 84 for ( int p = 0; p < PRIMITIVE_WIDENING_HIERARCHY.length; p++ ) { 85 Class<?> c = PRIMITIVE_WIDENING_HIERARCHY[p]; 86 addPrimitiveAndBoxed(result, c); 87 88 if ( c.equals(type) ) 89 break; 90 } 91 } 92 } 93 94 /** 95 * NPC = JLS 5.1.3 Narrowing Primitive Conversions 96 * + JLS 5.1.4 Widening and Narrowing Primitive Conversions 97 */ addNPCAssignableTypesFor(List<Class<?>> result, Class<?> type)98 private static void addNPCAssignableTypesFor(List<Class<?>> result, Class<?> type) { 99 // JLS 5.1.4 100 if ( type.equals(char.class) ) { 101 addPrimitiveAndBoxed(result, byte.class); 102 } 103 104 // JLS 5.1.3 105 int p = 0; 106 for ( ; p < PRIMITIVE_WIDENING_HIERARCHY.length; p++ ) { 107 if ( PRIMITIVE_WIDENING_HIERARCHY[p].equals(type) ) 108 break; 109 } 110 111 for ( ; p < PRIMITIVE_WIDENING_HIERARCHY.length; p++ ) { 112 addPrimitiveAndBoxed(result, PRIMITIVE_WIDENING_HIERARCHY[p]); 113 } 114 } 115 getAssignableTypesFor(Class<?> type)116 public static Class<?>[] getAssignableTypesFor(Class<?> type) { 117 if ( type.equals(void.class) ) 118 return new Class<?>[0]; 119 120 if ( type.isPrimitive() ) { 121 List<Class<?>> result = new LinkedList<Class<?>>(); 122 addPrimitiveAndBoxed(result, type); 123 addWPCAssignableTypesFor(result, type); 124 return (Class<?>[]) result.toArray(); 125 } 126 127 if ( type.equals(Object.class) ) 128 return new Class<?>[] { Object.class, String.class }; 129 130 if ( type.equals(String.class) ) 131 return new Class<?>[] { String.class }; 132 133 throw new IllegalArgumentException("Don't know how to handle type " + type); 134 } 135 getExplicitlyCastTypesFor(Class<?> type)136 public static Class<?>[] getExplicitlyCastTypesFor(Class<?> type) { 137 return TYPES; 138 } 139 canConvertType(Class<?> from, Class<?> to, boolean isRetType)140 public static boolean canConvertType(Class<?> from, Class<?> to, boolean isRetType) { 141 return (Boolean) convert(from, null, to, isRetType, true); 142 } 143 canExplicitlyCastType(Class<?> from, Class<?> to, boolean isRetType)144 public static boolean canExplicitlyCastType(Class<?> from, Class<?> to, boolean isRetType) { 145 return true; // TODO: can use explicitCaseArguments() to convert "from" to "to" 146 } 147 148 /** convert an argument according to the rules defined in MethodHandles.convertArguments() */ convertArgument(Argument from, Class<?> toType, boolean isRetType)149 public static Argument convertArgument(Argument from, Class<?> toType, boolean isRetType) throws ClassCastException { 150 Class<?> fromType = from.getType(); 151 152 if ( fromType.equals(toType) ) 153 return from; 154 155 Object toValue = convert(fromType, from.getValue(), toType, isRetType, false); 156 return new Argument(toType, toValue, from.isPreserved(), from.getTag()); 157 } 158 159 /** convert an argument according to the rules defined in MethodHandles.convertArguments() */ convert(Class<?> fromType, Object fromValue, Class<?> toType, boolean isRetType, boolean dryRun)160 private static Object convert(Class<?> fromType, Object fromValue, Class<?> toType, boolean isRetType, boolean dryRun) { 161 if ( ! dryRun ) { 162 if ( ! fromType.isPrimitive() ) 163 TestUtils.assertTrue(fromType.isAssignableFrom(fromValue.getClass()), "fromType " + fromType + " is not assignable from the type of fromValue " + fromValue); 164 else 165 TestUtils.assertTrue(BOX_MAP.get(fromType).isAssignableFrom(fromValue.getClass()), "Boxed fromType " + fromType + " is not assignable from the type of fromValue " + fromValue); 166 } 167 168 // JLS 5.1.1 Identity conversion 169 if ( fromType.equals(toType) ) 170 return dryRun ? Boolean.TRUE : fromValue; 171 172 Class<?> exactFromType = fromValue.getClass(); 173 174 Throwable cause = null; 175 176 try { 177 if ( isRetType ) { 178 // If the return type T1 is void, any returned value is discarded 179 if ( toType.equals(void.class) ) 180 return dryRun ? true : null; 181 182 // If the return type T0 is void and T1 a reference, a null value is introduced. 183 if ( fromType.equals(void.class) && ! toType.isPrimitive() ) 184 return dryRun ? true : null; 185 186 // If the return type T0 is void and T1 a primitive, a zero value is introduced. 187 if ( fromType.equals(void.class) && toType.isPrimitive() ) { 188 return dryRun ? true : BOX_MAP.get(toType).newInstance(); 189 } 190 } 191 192 // If T0 and T1 are references, then a cast to T1 is applied. 193 // (The types do not need to be related in any particular way.) 194 if ( ! fromType.isPrimitive() && ! toType.isPrimitive() ) { 195 return dryRun ? toType.isAssignableFrom(fromType) 196 : toType.cast(fromValue); 197 } 198 199 // If T0 and T1 are primitives, then a Java method invocation conversion 200 // (JLS 5.3) is applied, if one exists. 201 if ( fromType.isPrimitive() && toType.isPrimitive() ) { 202 if ( dryRun ) { 203 for ( Class<?> tt : getAssignableTypesFor(toType) ) { 204 if ( tt.equals(fromType) ) 205 return true; 206 } 207 208 return false; 209 } else { 210 return PrimitiveTypeConverter.convert(fromValue, toType); 211 } 212 } 213 214 // If T0 is a primitive and T1 a reference, a boxing conversion is applied 215 // if one exists, possibly followed by a reference conversion to a superclass. 216 // T1 must be a wrapper class or a supertype of one. 217 if ( fromType.isPrimitive() && ! toType.isPrimitive() ) { 218 return dryRun ? toType.isAssignableFrom(BOX_MAP.get(fromType)) 219 : toType.cast(fromType.cast(fromValue)); 220 } 221 222 // If T0 is a reference and T1 a primitive, an unboxing conversion will be applied 223 // at runtime, possibly followed by a Java method invocation conversion (JLS 5.3) 224 // on the primitive value. (These are the widening conversions.) T0 must be 225 // a wrapper class or a supertype of one. (In the case where T0 is Object, 226 // these are the conversions allowed by java.lang.reflect.Method.invoke.) 227 if ( ! fromType.isPrimitive() && toType.isPrimitive() ) { 228 if ( dryRun ) { 229 if ( ! BOX_MAP.values().contains(exactFromType) ) 230 return false; 231 232 233 } 234 235 return dryRun ? toType.isAssignableFrom(BOX_MAP.get(fromType)) 236 : toType.cast(fromType.cast(fromValue)); 237 } 238 239 } catch ( Throwable t ) { 240 cause = t; 241 } 242 243 if ( dryRun ) 244 return Boolean.FALSE; 245 else 246 throw (ClassCastException) (new ClassCastException("Can't convert value [" + fromValue + "] from type [" + fromType + "] to type [" + toType + "]")).initCause(cause); 247 } 248 explicitCastArgument(Argument from, Class<?> toType, boolean isRetType)249 public static Argument explicitCastArgument(Argument from, Class<?> toType, boolean isRetType) { 250 return from; // TODO 251 } 252 nextRandomValueForType(Class<?> type)253 public static Object nextRandomValueForType(Class<?> type) throws InstantiationException, IllegalAccessException { 254 if (type.equals(void.class)) 255 return null; 256 257 if (type.equals(boolean.class) || type.equals(Boolean.class)) 258 return Boolean.valueOf(Env.getRNG().nextInt(2) == 0); 259 260 if (type.equals(byte.class) || type.equals(Byte.class)) 261 return Byte.valueOf((byte) Env.getRNG().nextInt(1 << Byte.SIZE)); 262 263 if (type.equals(int.class) || type.equals(Integer.class)) 264 return Integer.valueOf(Env.getRNG().nextInt()); 265 266 if (type.equals(short.class) || type.equals(Short.class)) 267 return Short.valueOf((short) Env.getRNG().nextInt(1 << Short.SIZE)); 268 269 if (type.equals(long.class) || type.equals(Long.class)) 270 return Long.valueOf(Env.getRNG().nextLong()); 271 272 if (type.equals(float.class) || type.equals(Float.class)) 273 return Float.valueOf(Env.getRNG().nextFloat()); 274 275 if (type.equals(double.class) || type.equals(Double.class)) 276 return Double.valueOf(Env.getRNG().nextDouble()); 277 278 if (type.equals(char.class) || type.equals(Character.class)) 279 return Character.valueOf((char) (32 + Env.getRNG().nextInt(96))); 280 281 if (type.equals(Object.class)) 282 return new Object(); 283 284 if (type.equals(String.class)) { 285 StringBuilder sb = new StringBuilder(); 286 for (int i = Env.getRNG().nextInt(100); i > 0; i--) 287 sb.append(nextRandomValueForType(char.class)); 288 return sb.toString(); 289 } 290 291 throw new IllegalArgumentException("Don't know how to handle type " + type); 292 } 293 getSlotsCount(Class<?> type)294 public static int getSlotsCount(Class<?> type) { 295 if (type.equals(void.class)) 296 return 0; 297 298 if ( type.equals(boolean.class) || type.equals(Boolean.class) 299 || type.equals(byte.class) || type.equals(Byte.class) 300 || type.equals(int.class) || type.equals(Integer.class) 301 || type.equals(short.class) || type.equals(Short.class) 302 || type.equals(float.class) || type.equals(Float.class) 303 || type.equals(char.class) || type.equals(Character.class) 304 || Object.class.isAssignableFrom(type) ) 305 return 1; 306 307 if ( type.equals(long.class) || type.equals(Long.class) 308 || type.equals(double.class) || type.equals(Double.class)) 309 return 2; 310 311 throw new IllegalArgumentException("Don't know how to handle type " + type); 312 } 313 } 314