JavaArgumentConverters.java revision 1196:d0efd099521a
116359Sasami/* 216359Sasami * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 316359Sasami * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 416359Sasami * 516359Sasami * This code is free software; you can redistribute it and/or modify it 616359Sasami * under the terms of the GNU General Public License version 2 only, as 716359Sasami * published by the Free Software Foundation. Oracle designates this 816359Sasami * particular file as subject to the "Classpath" exception as provided 916359Sasami * by Oracle in the LICENSE file that accompanied this code. 1016359Sasami * 1116359Sasami * This code is distributed in the hope that it will be useful, but WITHOUT 1216359Sasami * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1316359Sasami * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1416359Sasami * version 2 for more details (a copy is included in the LICENSE file that 1516359Sasami * accompanied this code). 1616359Sasami * 1716359Sasami * You should have received a copy of the GNU General Public License version 1816359Sasami * 2 along with this work; if not, write to the Free Software Foundation, 1916359Sasami * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2016359Sasami * 2116359Sasami * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2216359Sasami * or visit www.oracle.com if you need additional information or have any 2316359Sasami * questions. 2416359Sasami */ 2516359Sasami 2616359Sasamipackage jdk.nashorn.internal.runtime.linker; 2716359Sasami 2816359Sasamiimport static jdk.nashorn.internal.lookup.Lookup.MH; 2950477Speterimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 3016359Sasamiimport static jdk.nashorn.internal.runtime.JSType.isString; 3145816Skatoimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 3216359Sasami 3316359Sasamiimport java.lang.invoke.MethodHandle; 3416359Sasamiimport java.lang.invoke.MethodHandles; 3531778Seivindimport java.util.HashMap; 3616359Sasamiimport java.util.Map; 3746871Skatoimport jdk.internal.dynalink.support.TypeUtilities; 3816359Sasamiimport jdk.nashorn.internal.runtime.ConsString; 3916359Sasamiimport jdk.nashorn.internal.runtime.JSType; 4016359Sasamiimport jdk.nashorn.internal.runtime.ScriptObject; 4116359Sasami 4216359Sasami/** 4316359Sasami * Utility class shared by {@code NashornLinker} and {@code NashornPrimitiveLinker} for converting JS values to Java 4416359Sasami * types. 4516359Sasami */ 4616359Sasamifinal class JavaArgumentConverters { 4716359Sasami 4816359Sasami private static final MethodHandle TO_BOOLEAN = findOwnMH("toBoolean", Boolean.class, Object.class); 4916359Sasami private static final MethodHandle TO_STRING = findOwnMH("toString", String.class, Object.class); 5016359Sasami private static final MethodHandle TO_DOUBLE = findOwnMH("toDouble", Double.class, Object.class); 5116359Sasami private static final MethodHandle TO_NUMBER = findOwnMH("toNumber", Number.class, Object.class); 5216359Sasami private static final MethodHandle TO_LONG = findOwnMH("toLong", Long.class, Object.class); 5316359Sasami private static final MethodHandle TO_LONG_PRIMITIVE = findOwnMH("toLongPrimitive", long.class, Object.class); 5416359Sasami private static final MethodHandle TO_CHAR = findOwnMH("toChar", Character.class, Object.class); 5516359Sasami private static final MethodHandle TO_CHAR_PRIMITIVE = findOwnMH("toCharPrimitive", char.class, Object.class); 5616359Sasami 5716359Sasami private JavaArgumentConverters() { 5816359Sasami } 5916359Sasami 6016359Sasami static MethodHandle getConverter(final Class<?> targetType) { 6116359Sasami return CONVERTERS.get(targetType); 6216359Sasami } 6316359Sasami 6416359Sasami @SuppressWarnings("unused") 6516359Sasami private static Boolean toBoolean(final Object obj) { 6616359Sasami if (obj instanceof Boolean) { 6716359Sasami return (Boolean) obj; 6816359Sasami } 6916359Sasami 7016359Sasami if (obj == null) { 7116359Sasami // NOTE: FindBugs complains here about the NP_BOOLEAN_RETURN_NULL pattern: we're returning null from a 7216359Sasami // method that has a return type of Boolean, as it is worried about a NullPointerException if there's a 7342262Skato // conversion to a primitive boolean. We know what we're doing, though. We're using a separate method when 7442262Skato // we're converting Object to a primitive boolean - see how the CONVERTERS map is populated. We specifically 7542262Skato // want to have null and Undefined to be converted to a (Boolean)null when being passed to a Java method 7654174Snyan // that expects a Boolean argument. 7754174Snyan // TODO: if/when we're allowed to use FindBugs at build time, we can use annotations to disable this warning 7854174Snyan return null; 7942262Skato } 8016359Sasami 8176212Skato if (obj == UNDEFINED) { 8265877Skato // NOTE: same reasoning for FindBugs NP_BOOLEAN_RETURN_NULL warning as in the preceding comment. 8316359Sasami return null; 8424132Sbde } 8538297Skato 8616359Sasami if (obj instanceof Number) { 87114216Skan final double num = ((Number) obj).doubleValue(); 8876212Skato return num != 0 && !Double.isNaN(num); 8976212Skato } 9076212Skato 9176212Skato if (isString(obj)) { 9276212Skato return ((CharSequence) obj).length() > 0; 9376212Skato } 94131125Snyan 9576212Skato if (obj instanceof ScriptObject) { 9616359Sasami return true; 9776212Skato } 9845783Skato 9945783Skato throw assertUnexpectedType(obj); 10045226Skato } 10193934Snyan 102119525Snyan private static Character toChar(final Object o) { 103119525Snyan if (o == null) { 104119525Snyan return null; 105119525Snyan } 10616359Sasami 10745783Skato if (o instanceof Number) { 10816359Sasami final int ival = ((Number)o).intValue(); 10945783Skato if (ival >= Character.MIN_VALUE && ival <= Character.MAX_VALUE) { 11045783Skato return Character.valueOf((char) ival); 11185302Simp } 11286912Snyan 11345783Skato throw typeError("cant.convert.number.to.char"); 11486912Snyan } 11586912Snyan 11686912Snyan final String s = toString(o); 11786912Snyan if (s == null) { 11886912Snyan return null; 11916359Sasami } 12077962Snyan 12116359Sasami if (s.length() != 1) { 12277962Snyan throw typeError("cant.convert.string.to.char"); 12342265Skato } 12477962Snyan 12577962Snyan return s.charAt(0); 12642265Skato } 12716359Sasami 12816359Sasami static char toCharPrimitive(final Object obj0) { 12916359Sasami final Character c = toChar(obj0); 13016359Sasami return c == null ? (char)0 : c; 13116359Sasami } 13216359Sasami 13316359Sasami // Almost identical to ScriptRuntime.toString, but returns null for null instead of the string "null". 13416359Sasami static String toString(final Object obj) { 13593934Snyan return obj == null ? null : JSType.toString(obj); 13693934Snyan } 13793934Snyan 13893934Snyan @SuppressWarnings("unused") 13916359Sasami private static Double toDouble(final Object obj0) { 140128796Snyan // TODO - Order tests for performance. 141128796Snyan for (Object obj = obj0; ;) { 142128796Snyan if (obj == null) { 143128796Snyan return null; 144128796Snyan } else if (obj instanceof Double) { 145128796Snyan return (Double) obj; 146128796Snyan } else if (obj instanceof Number) { 147128796Snyan return ((Number)obj).doubleValue(); 148128796Snyan } else if (obj instanceof String) { 149128796Snyan return JSType.toNumber((String) obj); 150128796Snyan } else if (obj instanceof ConsString) { 151128796Snyan return JSType.toNumber(obj.toString()); 152128796Snyan } else if (obj instanceof Boolean) { 153128796Snyan return (Boolean) obj ? 1 : +0.0; 154128796Snyan } else if (obj instanceof ScriptObject) { 155128796Snyan obj = JSType.toPrimitive(obj, Number.class); 156128796Snyan continue; 15716359Sasami } else if (obj == UNDEFINED) { 15816359Sasami return Double.NaN; 15916359Sasami } 16016359Sasami throw assertUnexpectedType(obj); 16145783Skato } 16245783Skato } 163128796Snyan 16445783Skato @SuppressWarnings("unused") 165128796Snyan private static Number toNumber(final Object obj0) { 166104134Snyan // TODO - Order tests for performance. 167104134Snyan for (Object obj = obj0; ;) { 16816359Sasami if (obj == null) { 16916359Sasami return null; 170120809Snyan } else if (obj instanceof Number) { 17145783Skato return (Number) obj; 172120809Snyan } else if (obj instanceof String) { 173128796Snyan return JSType.toNumber((String) obj); 174120809Snyan } else if (obj instanceof ConsString) { 175128796Snyan return JSType.toNumber(obj.toString()); 17645783Skato } else if (obj instanceof Boolean) { 177120809Snyan return (Boolean) obj ? 1 : +0.0; 17845783Skato } else if (obj instanceof ScriptObject) { 17945783Skato obj = JSType.toPrimitive(obj, Number.class); 180120809Snyan continue; 181128796Snyan } else if (obj == UNDEFINED) { 182120809Snyan return Double.NaN; 183128796Snyan } 184112032Snyan throw assertUnexpectedType(obj); 185128796Snyan } 186120809Snyan } 187120809Snyan 188128796Snyan private static Long toLong(final Object obj0) { 18916359Sasami // TODO - Order tests for performance. 19060472Snyan for (Object obj = obj0; ;) { 19160472Snyan if (obj == null) { 19260472Snyan return null; 19360472Snyan } else if (obj instanceof Long) { 19460472Snyan return (Long) obj; 19516359Sasami } else if (obj instanceof Integer) { 19616359Sasami return ((Integer)obj).longValue(); 19716359Sasami } else if (obj instanceof Double) { 19816359Sasami final Double d = (Double)obj; 19916359Sasami if(Double.isInfinite(d.doubleValue())) { 20016359Sasami return 0L; 20116359Sasami } 20216359Sasami return d.longValue(); 20351654Sphk } else if (obj instanceof Float) { 20416359Sasami final Float f = (Float)obj; 20516359Sasami if(Float.isInfinite(f.floatValue())) { 20616359Sasami return 0L; 20716359Sasami } 20816359Sasami return f.longValue(); 20916359Sasami } else if (obj instanceof Number) { 21016359Sasami return ((Number)obj).longValue(); 21116359Sasami } else if (isString(obj)) { 21216359Sasami return JSType.toLong(obj); 21316359Sasami } else if (obj instanceof Boolean) { 21416359Sasami return (Boolean)obj ? 1L : 0L; 21516359Sasami } else if (obj instanceof ScriptObject) { 21616359Sasami obj = JSType.toPrimitive(obj, Number.class); 21716359Sasami continue; 21816359Sasami } else if (obj == UNDEFINED) { 21925026Skato return null; // null or 0L? 22016359Sasami } 22116359Sasami throw assertUnexpectedType(obj); 22216359Sasami } 22316359Sasami } 22416359Sasami 22516359Sasami private static AssertionError assertUnexpectedType(final Object obj) { 22616359Sasami return new AssertionError("Unexpected type" + obj.getClass().getName() + ". Guards should have prevented this"); 22716359Sasami } 22816359Sasami 22916359Sasami @SuppressWarnings("unused") 23016359Sasami private static long toLongPrimitive(final Object obj0) { 23116359Sasami final Long l = toLong(obj0); 23216359Sasami return l == null ? 0L : l; 23316359Sasami } 23416359Sasami 23516359Sasami private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) { 23616359Sasami return MH.findStatic(MethodHandles.lookup(), JavaArgumentConverters.class, name, MH.type(rtype, types)); 23716359Sasami } 23816359Sasami 23916359Sasami private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>(); 24016359Sasami 24116359Sasami static { 24216359Sasami CONVERTERS.put(Number.class, TO_NUMBER); 24316359Sasami CONVERTERS.put(String.class, TO_STRING); 24416359Sasami 24516359Sasami CONVERTERS.put(boolean.class, JSType.TO_BOOLEAN.methodHandle()); 24616359Sasami CONVERTERS.put(Boolean.class, TO_BOOLEAN); 24716359Sasami 24816359Sasami CONVERTERS.put(char.class, TO_CHAR_PRIMITIVE); 24916359Sasami CONVERTERS.put(Character.class, TO_CHAR); 25016359Sasami 25116359Sasami CONVERTERS.put(double.class, JSType.TO_NUMBER.methodHandle()); 25216359Sasami CONVERTERS.put(Double.class, TO_DOUBLE); 25325026Skato 25416359Sasami CONVERTERS.put(long.class, TO_LONG_PRIMITIVE); 25516359Sasami CONVERTERS.put(Long.class, TO_LONG); 25616359Sasami 25716359Sasami putLongConverter(Byte.class); 25816359Sasami putLongConverter(Short.class); 25916359Sasami putLongConverter(Integer.class); 26016359Sasami putDoubleConverter(Float.class); 26116359Sasami 26216359Sasami } 26316359Sasami 26416359Sasami private static void putDoubleConverter(final Class<?> targetType) { 265120809Snyan final Class<?> primitive = TypeUtilities.getPrimitiveType(targetType); 26616359Sasami CONVERTERS.put(primitive, MH.explicitCastArguments(JSType.TO_NUMBER.methodHandle(), JSType.TO_NUMBER.methodHandle().type().changeReturnType(primitive))); 26716359Sasami CONVERTERS.put(targetType, MH.filterReturnValue(TO_DOUBLE, findOwnMH(primitive.getName() + "Value", targetType, Double.class))); 268120809Snyan } 26916359Sasami 27016359Sasami private static void putLongConverter(final Class<?> targetType) { 27116359Sasami final Class<?> primitive = TypeUtilities.getPrimitiveType(targetType); 27216359Sasami CONVERTERS.put(primitive, MH.explicitCastArguments(TO_LONG_PRIMITIVE, TO_LONG_PRIMITIVE.type().changeReturnType(primitive))); 27316359Sasami CONVERTERS.put(targetType, MH.filterReturnValue(TO_LONG, findOwnMH(primitive.getName() + "Value", targetType, Long.class))); 27416359Sasami } 27516359Sasami 27616359Sasami @SuppressWarnings("unused") 27716359Sasami private static Byte byteValue(final Long l) { 27816359Sasami return l == null ? null : l.byteValue(); 27916359Sasami } 28016359Sasami 28116359Sasami @SuppressWarnings("unused") 28216359Sasami private static Short shortValue(final Long l) { 28343663Skato return l == null ? null : l.shortValue(); 28416359Sasami } 28516359Sasami 28643663Skato @SuppressWarnings("unused") 28743663Skato private static Integer intValue(final Long l) { 28816359Sasami return l == null ? null : l.intValue(); 28916359Sasami } 29016359Sasami 29116359Sasami @SuppressWarnings("unused") 29260472Snyan private static Float floatValue(final Double d) { 29360472Snyan return d == null ? null : d.floatValue(); 29460472Snyan } 29516359Sasami 29616359Sasami} 29716359Sasami