/* * 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> BOX_MAP = new HashMap, Class>(); public static final Map, Class> UNBOX_MAP = new HashMap, 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> 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> 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> 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> 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> result = new LinkedList>(); 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); } }