| /* |
| * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package vm.mlvm.meth.share; |
| |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import nsk.share.test.TestUtils; |
| import vm.mlvm.share.Env; |
| |
| public class TestTypes { |
| |
| public static final Class<?>[] TYPES = { |
| // void.class |
| boolean.class, byte.class, char.class, short.class, int.class, long.class, |
| float.class, double.class, Object.class, String.class |
| }; |
| |
| public static final Map<Class<?>, Class<?>> BOX_MAP = new HashMap<Class<?>, Class<?>>(); |
| public static final Map<Class<?>, Class<?>> UNBOX_MAP = new HashMap<Class<?>, Class<?>>(); |
| static { |
| BOX_MAP.put(boolean.class, Boolean.class); |
| BOX_MAP.put(byte.class, Byte.class); |
| BOX_MAP.put(char.class, Character.class); |
| BOX_MAP.put(short.class, Short.class); |
| BOX_MAP.put(int.class, Integer.class); |
| BOX_MAP.put(long.class, Long.class); |
| BOX_MAP.put(float.class, Float.class); |
| BOX_MAP.put(double.class, Double.class); |
| |
| for ( Entry<Class<?>, Class<?>> e : BOX_MAP.entrySet() ) { |
| UNBOX_MAP.put(e.getValue(), e.getKey()); |
| } |
| } |
| |
| public static final Class<?>[] PRIMITIVE_WIDENING_HIERARCHY = { |
| byte.class, short.class, char.class, int.class, long.class, float.class, double.class |
| }; |
| |
| public static boolean isBoxedType(Class<?> type) { |
| return BOX_MAP.values().contains(type); |
| } |
| |
| private static void addPrimitiveAndBoxed(List<Class<?>> list, Class<?> type) { |
| list.add(type); |
| list.add(BOX_MAP.get(type)); |
| } |
| |
| /** |
| * WPC = JLS 5.1.2 Widening Primitive Conversions |
| * @param type |
| * @return |
| */ |
| private static void addWPCAssignableTypesFor(List<Class<?>> result, Class<?> type) { |
| if ( type.equals(short.class) ) { |
| addPrimitiveAndBoxed(result, byte.class); |
| } |
| |
| if ( type.equals(int.class) || type.equals(long.class) || type.equals(float.class) || type.equals(double.class) ) { |
| for ( int p = 0; p < PRIMITIVE_WIDENING_HIERARCHY.length; p++ ) { |
| Class<?> c = PRIMITIVE_WIDENING_HIERARCHY[p]; |
| addPrimitiveAndBoxed(result, c); |
| |
| if ( c.equals(type) ) |
| break; |
| } |
| } |
| } |
| |
| /** |
| * NPC = JLS 5.1.3 Narrowing Primitive Conversions |
| * + JLS 5.1.4 Widening and Narrowing Primitive Conversions |
| */ |
| private static void addNPCAssignableTypesFor(List<Class<?>> result, Class<?> type) { |
| // JLS 5.1.4 |
| if ( type.equals(char.class) ) { |
| addPrimitiveAndBoxed(result, byte.class); |
| } |
| |
| // JLS 5.1.3 |
| int p = 0; |
| for ( ; p < PRIMITIVE_WIDENING_HIERARCHY.length; p++ ) { |
| if ( PRIMITIVE_WIDENING_HIERARCHY[p].equals(type) ) |
| break; |
| } |
| |
| for ( ; p < PRIMITIVE_WIDENING_HIERARCHY.length; p++ ) { |
| addPrimitiveAndBoxed(result, PRIMITIVE_WIDENING_HIERARCHY[p]); |
| } |
| } |
| |
| public static Class<?>[] getAssignableTypesFor(Class<?> type) { |
| if ( type.equals(void.class) ) |
| return new Class<?>[0]; |
| |
| if ( type.isPrimitive() ) { |
| List<Class<?>> result = new LinkedList<Class<?>>(); |
| addPrimitiveAndBoxed(result, type); |
| addWPCAssignableTypesFor(result, type); |
| return (Class<?>[]) result.toArray(); |
| } |
| |
| if ( type.equals(Object.class) ) |
| return new Class<?>[] { Object.class, String.class }; |
| |
| if ( type.equals(String.class) ) |
| return new Class<?>[] { String.class }; |
| |
| throw new IllegalArgumentException("Don't know how to handle type " + type); |
| } |
| |
| public static Class<?>[] getExplicitlyCastTypesFor(Class<?> type) { |
| return TYPES; |
| } |
| |
| public static boolean canConvertType(Class<?> from, Class<?> to, boolean isRetType) { |
| return (Boolean) convert(from, null, to, isRetType, true); |
| } |
| |
| public static boolean canExplicitlyCastType(Class<?> from, Class<?> to, boolean isRetType) { |
| return true; // TODO: can use explicitCaseArguments() to convert "from" to "to" |
| } |
| |
| /** convert an argument according to the rules defined in MethodHandles.convertArguments() */ |
| public static Argument convertArgument(Argument from, Class<?> toType, boolean isRetType) throws ClassCastException { |
| Class<?> fromType = from.getType(); |
| |
| if ( fromType.equals(toType) ) |
| return from; |
| |
| Object toValue = convert(fromType, from.getValue(), toType, isRetType, false); |
| return new Argument(toType, toValue, from.isPreserved(), from.getTag()); |
| } |
| |
| /** convert an argument according to the rules defined in MethodHandles.convertArguments() */ |
| private static Object convert(Class<?> fromType, Object fromValue, Class<?> toType, boolean isRetType, boolean dryRun) { |
| if ( ! dryRun ) { |
| if ( ! fromType.isPrimitive() ) |
| TestUtils.assertTrue(fromType.isAssignableFrom(fromValue.getClass()), "fromType " + fromType + " is not assignable from the type of fromValue " + fromValue); |
| else |
| TestUtils.assertTrue(BOX_MAP.get(fromType).isAssignableFrom(fromValue.getClass()), "Boxed fromType " + fromType + " is not assignable from the type of fromValue " + fromValue); |
| } |
| |
| // JLS 5.1.1 Identity conversion |
| if ( fromType.equals(toType) ) |
| return dryRun ? Boolean.TRUE : fromValue; |
| |
| Class<?> exactFromType = fromValue.getClass(); |
| |
| Throwable cause = null; |
| |
| try { |
| if ( isRetType ) { |
| // If the return type T1 is void, any returned value is discarded |
| if ( toType.equals(void.class) ) |
| return dryRun ? true : null; |
| |
| // If the return type T0 is void and T1 a reference, a null value is introduced. |
| if ( fromType.equals(void.class) && ! toType.isPrimitive() ) |
| return dryRun ? true : null; |
| |
| // If the return type T0 is void and T1 a primitive, a zero value is introduced. |
| if ( fromType.equals(void.class) && toType.isPrimitive() ) { |
| return dryRun ? true : BOX_MAP.get(toType).newInstance(); |
| } |
| } |
| |
| // If T0 and T1 are references, then a cast to T1 is applied. |
| // (The types do not need to be related in any particular way.) |
| if ( ! fromType.isPrimitive() && ! toType.isPrimitive() ) { |
| return dryRun ? toType.isAssignableFrom(fromType) |
| : toType.cast(fromValue); |
| } |
| |
| // If T0 and T1 are primitives, then a Java method invocation conversion |
| // (JLS 5.3) is applied, if one exists. |
| if ( fromType.isPrimitive() && toType.isPrimitive() ) { |
| if ( dryRun ) { |
| for ( Class<?> tt : getAssignableTypesFor(toType) ) { |
| if ( tt.equals(fromType) ) |
| return true; |
| } |
| |
| return false; |
| } else { |
| return PrimitiveTypeConverter.convert(fromValue, toType); |
| } |
| } |
| |
| // If T0 is a primitive and T1 a reference, a boxing conversion is applied |
| // if one exists, possibly followed by a reference conversion to a superclass. |
| // T1 must be a wrapper class or a supertype of one. |
| if ( fromType.isPrimitive() && ! toType.isPrimitive() ) { |
| return dryRun ? toType.isAssignableFrom(BOX_MAP.get(fromType)) |
| : toType.cast(fromType.cast(fromValue)); |
| } |
| |
| // If T0 is a reference and T1 a primitive, an unboxing conversion will be applied |
| // at runtime, possibly followed by a Java method invocation conversion (JLS 5.3) |
| // on the primitive value. (These are the widening conversions.) T0 must be |
| // a wrapper class or a supertype of one. (In the case where T0 is Object, |
| // these are the conversions allowed by java.lang.reflect.Method.invoke.) |
| if ( ! fromType.isPrimitive() && toType.isPrimitive() ) { |
| if ( dryRun ) { |
| if ( ! BOX_MAP.values().contains(exactFromType) ) |
| return false; |
| |
| |
| } |
| |
| return dryRun ? toType.isAssignableFrom(BOX_MAP.get(fromType)) |
| : toType.cast(fromType.cast(fromValue)); |
| } |
| |
| } catch ( Throwable t ) { |
| cause = t; |
| } |
| |
| if ( dryRun ) |
| return Boolean.FALSE; |
| else |
| throw (ClassCastException) (new ClassCastException("Can't convert value [" + fromValue + "] from type [" + fromType + "] to type [" + toType + "]")).initCause(cause); |
| } |
| |
| public static Argument explicitCastArgument(Argument from, Class<?> toType, boolean isRetType) { |
| return from; // TODO |
| } |
| |
| public static Object nextRandomValueForType(Class<?> type) throws InstantiationException, IllegalAccessException { |
| if (type.equals(void.class)) |
| return null; |
| |
| if (type.equals(boolean.class) || type.equals(Boolean.class)) |
| return new Boolean(Env.getRNG().nextInt(2) == 0); |
| |
| if (type.equals(byte.class) || type.equals(Byte.class)) |
| return new Byte((byte) Env.getRNG().nextInt(1 << Byte.SIZE)); |
| |
| if (type.equals(int.class) || type.equals(Integer.class)) |
| return new Integer(Env.getRNG().nextInt()); |
| |
| if (type.equals(short.class) || type.equals(Short.class)) |
| return new Short((short) Env.getRNG().nextInt(1 << Short.SIZE)); |
| |
| if (type.equals(long.class) || type.equals(Long.class)) |
| return new Long(Env.getRNG().nextLong()); |
| |
| if (type.equals(float.class) || type.equals(Float.class)) |
| return new Float(Env.getRNG().nextFloat()); |
| |
| if (type.equals(double.class) || type.equals(Double.class)) |
| return new Double(Env.getRNG().nextDouble()); |
| |
| if (type.equals(char.class) || type.equals(Character.class)) |
| return new Character((char) (32 + Env.getRNG().nextInt(96))); |
| |
| if (type.equals(Object.class)) |
| return new Object(); |
| |
| if (type.equals(String.class)) { |
| StringBuilder sb = new StringBuilder(); |
| for (int i = Env.getRNG().nextInt(100); i > 0; i--) |
| sb.append(nextRandomValueForType(char.class)); |
| return sb.toString(); |
| } |
| |
| throw new IllegalArgumentException("Don't know how to handle type " + type); |
| } |
| |
| public static int getSlotsCount(Class<?> type) { |
| if (type.equals(void.class)) |
| return 0; |
| |
| if ( type.equals(boolean.class) || type.equals(Boolean.class) |
| || type.equals(byte.class) || type.equals(Byte.class) |
| || type.equals(int.class) || type.equals(Integer.class) |
| || type.equals(short.class) || type.equals(Short.class) |
| || type.equals(float.class) || type.equals(Float.class) |
| || type.equals(char.class) || type.equals(Character.class) |
| || Object.class.isAssignableFrom(type) ) |
| return 1; |
| |
| if ( type.equals(long.class) || type.equals(Long.class) |
| || type.equals(double.class) || type.equals(Double.class)) |
| return 2; |
| |
| throw new IllegalArgumentException("Don't know how to handle type " + type); |
| } |
| } |