blob: e04466c9f38395ba9ac02e853dfc3308fd8c4d93 [file] [log] [blame]
/*
* 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);
}
}