Fri, 20 Feb 2015 15:47:28 +0100
8072426: Can't compare Java objects to strings or numbers
Reviewed-by: hannesw, lagergren, sundar
1.1 --- a/src/jdk/nashorn/api/scripting/AbstractJSObject.java Mon Mar 09 11:34:48 2015 +0100 1.2 +++ b/src/jdk/nashorn/api/scripting/AbstractJSObject.java Fri Feb 20 15:47:28 2015 +0100 1.3 @@ -28,6 +28,7 @@ 1.4 import java.util.Collection; 1.5 import java.util.Collections; 1.6 import java.util.Set; 1.7 +import jdk.nashorn.internal.runtime.JSType; 1.8 1.9 /** 1.10 * This is the base class for nashorn ScriptObjectMirror class. 1.11 @@ -161,9 +162,8 @@ 1.12 * @return set of property names 1.13 */ 1.14 @Override 1.15 - @SuppressWarnings("unchecked") 1.16 public Set<String> keySet() { 1.17 - return Collections.EMPTY_SET; 1.18 + return Collections.emptySet(); 1.19 } 1.20 1.21 /** 1.22 @@ -172,9 +172,8 @@ 1.23 * @return set of property values. 1.24 */ 1.25 @Override 1.26 - @SuppressWarnings("unchecked") 1.27 public Collection<Object> values() { 1.28 - return Collections.EMPTY_SET; 1.29 + return Collections.emptySet(); 1.30 } 1.31 1.32 // JavaScript instanceof check 1.33 @@ -249,9 +248,41 @@ 1.34 * Returns this object's numeric value. 1.35 * 1.36 * @return this object's numeric value. 1.37 + * @deprecated use {@link #getDefaultValue(Class)} with {@link Number} hint instead. 1.38 */ 1.39 - @Override 1.40 + @Override @Deprecated 1.41 public double toNumber() { 1.42 - return Double.NaN; 1.43 + return JSType.toNumber(JSType.toPrimitive(this, Number.class)); 1.44 + } 1.45 + 1.46 + /** 1.47 + * Implements this object's {@code [[DefaultValue]]} method. The default implementation follows ECMAScript 5.1 1.48 + * section 8.6.2 but subclasses are free to provide their own implementations. 1.49 + * 1.50 + * @param hint the type hint. Should be either {@code null}, {@code Number.class} or {@code String.class}. 1.51 + * @return this object's default value. 1.52 + * @throws UnsupportedOperationException if the conversion can't be performed. The engine will convert this 1.53 + * exception into a JavaScript {@code TypeError}. 1.54 + */ 1.55 + public Object getDefaultValue(final Class<?> hint) { 1.56 + return DefaultValueImpl.getDefaultValue(this, hint); 1.57 + } 1.58 + 1.59 + /** 1.60 + * When passed an {@link AbstractJSObject}, invokes its {@link #getDefaultValue(Class)} method. When passed any 1.61 + * other {@link JSObject}, it will obtain its {@code [[DefaultValue]]} method as per ECMAScript 5.1 section 1.62 + * 8.6.2. 1.63 + * 1.64 + * @param jsobj the {@link JSObject} whose {@code [[DefaultValue]]} is obtained. 1.65 + * @param hint the type hint. Should be either {@code null}, {@code Number.class} or {@code String.class}. 1.66 + * @return this object's default value. 1.67 + * @throws UnsupportedOperationException if the conversion can't be performed. The engine will convert this 1.68 + * exception into a JavaScript {@code TypeError}. 1.69 + */ 1.70 + public static Object getDefaultValue(final JSObject jsobj, final Class<?> hint) { 1.71 + if (jsobj instanceof AbstractJSObject) { 1.72 + return ((AbstractJSObject)jsobj).getDefaultValue(hint); 1.73 + } 1.74 + return DefaultValueImpl.getDefaultValue(jsobj, hint); 1.75 } 1.76 }
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/src/jdk/nashorn/api/scripting/DefaultValueImpl.java Fri Feb 20 15:47:28 2015 +0100 2.3 @@ -0,0 +1,55 @@ 2.4 +/* 2.5 + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 2.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.7 + * 2.8 + * This code is free software; you can redistribute it and/or modify it 2.9 + * under the terms of the GNU General Public License version 2 only, as 2.10 + * published by the Free Software Foundation. Oracle designates this 2.11 + * particular file as subject to the "Classpath" exception as provided 2.12 + * by Oracle in the LICENSE file that accompanied this code. 2.13 + * 2.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 2.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 2.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 2.17 + * version 2 for more details (a copy is included in the LICENSE file that 2.18 + * accompanied this code). 2.19 + * 2.20 + * You should have received a copy of the GNU General Public License version 2.21 + * 2 along with this work; if not, write to the Free Software Foundation, 2.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2.23 + * 2.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2.25 + * or visit www.oracle.com if you need additional information or have any 2.26 + * questions. 2.27 + */ 2.28 + 2.29 +package jdk.nashorn.api.scripting; 2.30 + 2.31 +import jdk.nashorn.internal.runtime.JSType; 2.32 + 2.33 +/** 2.34 + * Default implementation of {@link JSObject#getDefaultValue(Class)}. Isolated into a separate class mostly so 2.35 + * that we can have private static instances of function name arrays, something we couldn't declare without it 2.36 + * being visible in {@link JSObject} interface. 2.37 + */ 2.38 +class DefaultValueImpl { 2.39 + private static final String[] DEFAULT_VALUE_FNS_NUMBER = new String[] { "valueOf", "toString" }; 2.40 + private static final String[] DEFAULT_VALUE_FNS_STRING = new String[] { "toString", "valueOf" }; 2.41 + 2.42 + static Object getDefaultValue(final JSObject jsobj, final Class<?> hint) throws UnsupportedOperationException { 2.43 + final boolean isNumber = hint == null || hint == Number.class; 2.44 + for(final String methodName: isNumber ? DEFAULT_VALUE_FNS_NUMBER : DEFAULT_VALUE_FNS_STRING) { 2.45 + final Object objMember = jsobj.getMember(methodName); 2.46 + if (objMember instanceof JSObject) { 2.47 + final JSObject member = (JSObject)objMember; 2.48 + if (member.isFunction()) { 2.49 + final Object value = member.call(jsobj); 2.50 + if (JSType.isPrimitive(value)) { 2.51 + return value; 2.52 + } 2.53 + } 2.54 + } 2.55 + } 2.56 + throw new UnsupportedOperationException(isNumber ? "cannot.get.default.number" : "cannot.get.default.string"); 2.57 + } 2.58 +}
3.1 --- a/src/jdk/nashorn/api/scripting/JSObject.java Mon Mar 09 11:34:48 2015 +0100 3.2 +++ b/src/jdk/nashorn/api/scripting/JSObject.java Fri Feb 20 15:47:28 2015 +0100 3.3 @@ -186,6 +186,8 @@ 3.4 * Returns this object's numeric value. 3.5 * 3.6 * @return this object's numeric value. 3.7 + * @deprecated use {@link AbstractJSObject#getDefaultValue(JSObject, Class)} with {@link Number} hint instead. 3.8 */ 3.9 + @Deprecated 3.10 public double toNumber(); 3.11 }
4.1 --- a/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Mon Mar 09 11:34:48 2015 +0100 4.2 +++ b/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java Fri Feb 20 15:47:28 2015 +0100 4.3 @@ -46,6 +46,7 @@ 4.4 import jdk.nashorn.internal.objects.Global; 4.5 import jdk.nashorn.internal.runtime.ConsString; 4.6 import jdk.nashorn.internal.runtime.Context; 4.7 +import jdk.nashorn.internal.runtime.ECMAException; 4.8 import jdk.nashorn.internal.runtime.JSType; 4.9 import jdk.nashorn.internal.runtime.ScriptFunction; 4.10 import jdk.nashorn.internal.runtime.ScriptObject; 4.11 @@ -812,7 +813,7 @@ 4.12 } 4.13 } 4.14 4.15 - @Override 4.16 + @Override @Deprecated 4.17 public double toNumber() { 4.18 return inGlobal(new Callable<Double>() { 4.19 @Override public Double call() { 4.20 @@ -820,4 +821,21 @@ 4.21 } 4.22 }); 4.23 } 4.24 + 4.25 + @Override 4.26 + public Object getDefaultValue(final Class<?> hint) { 4.27 + return inGlobal(new Callable<Object>() { 4.28 + @Override public Object call() { 4.29 + try { 4.30 + return sobj.getDefaultValue(hint); 4.31 + } catch (final ECMAException e) { 4.32 + // We're catching ECMAException (likely TypeError), and translating it to 4.33 + // UnsupportedOperationException. This in turn will be translated into TypeError of the 4.34 + // caller's Global by JSType#toPrimitive(JSObject,Class) therefore ensuring that it's 4.35 + // recognized as "instanceof TypeError" in the caller. 4.36 + throw new UnsupportedOperationException(e.getMessage(), e); 4.37 + } 4.38 + } 4.39 + }); 4.40 + } 4.41 }
5.1 --- a/src/jdk/nashorn/internal/runtime/JSType.java Mon Mar 09 11:34:48 2015 +0100 5.2 +++ b/src/jdk/nashorn/internal/runtime/JSType.java Fri Feb 20 15:47:28 2015 +0100 5.3 @@ -29,6 +29,7 @@ 5.4 import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY; 5.5 import static jdk.nashorn.internal.lookup.Lookup.MH; 5.6 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 5.7 + 5.8 import java.lang.invoke.MethodHandle; 5.9 import java.lang.invoke.MethodHandles; 5.10 import java.lang.reflect.Array; 5.11 @@ -37,6 +38,7 @@ 5.12 import java.util.Deque; 5.13 import java.util.List; 5.14 import jdk.internal.dynalink.beans.StaticClass; 5.15 +import jdk.nashorn.api.scripting.AbstractJSObject; 5.16 import jdk.nashorn.api.scripting.JSObject; 5.17 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 5.18 import jdk.nashorn.internal.codegen.types.Type; 5.19 @@ -210,7 +212,6 @@ 5.20 /** Method handle for void returns. */ 5.21 public static final Call VOID_RETURN = staticCall(JSTYPE_LOOKUP, JSType.class, "voidReturn", void.class); 5.22 5.23 - 5.24 /** 5.25 * The list of available accessor types in width order. This order is used for type guesses narrow{@literal ->} wide 5.26 * in the dual--fields world 5.27 @@ -480,17 +481,47 @@ 5.28 * @return the primitive form of the object 5.29 */ 5.30 public static Object toPrimitive(final Object obj, final Class<?> hint) { 5.31 - return obj instanceof ScriptObject ? toPrimitive((ScriptObject)obj, hint) : obj; 5.32 + if (obj instanceof ScriptObject) { 5.33 + return toPrimitive((ScriptObject)obj, hint); 5.34 + } else if (isPrimitive(obj)) { 5.35 + return obj; 5.36 + } else if (obj instanceof JSObject) { 5.37 + return toPrimitive((JSObject)obj, hint); 5.38 + } else if (obj instanceof StaticClass) { 5.39 + final String name = ((StaticClass)obj).getRepresentedClass().getName(); 5.40 + return new StringBuilder(12 + name.length()).append("[JavaClass ").append(name).append(']').toString(); 5.41 + } 5.42 + return obj.toString(); 5.43 } 5.44 5.45 private static Object toPrimitive(final ScriptObject sobj, final Class<?> hint) { 5.46 - final Object result = sobj.getDefaultValue(hint); 5.47 + return requirePrimitive(sobj.getDefaultValue(hint)); 5.48 + } 5.49 5.50 + private static Object requirePrimitive(final Object result) { 5.51 if (!isPrimitive(result)) { 5.52 throw typeError("bad.default.value", result.toString()); 5.53 } 5.54 + return result; 5.55 + } 5.56 5.57 - return result; 5.58 + /** 5.59 + * Primitive converter for a {@link JSObject} including type hint. Invokes 5.60 + * {@link AbstractJSObject#getDefaultValue(JSObject, Class)} and translates any thrown 5.61 + * {@link UnsupportedOperationException} to an ECMAScript {@code TypeError}. 5.62 + * See ECMA 9.1 ToPrimitive 5.63 + * 5.64 + * @param jsobj a JSObject 5.65 + * @param hint a type hint 5.66 + * 5.67 + * @return the primitive form of the JSObject 5.68 + */ 5.69 + public static Object toPrimitive(final JSObject jsobj, final Class<?> hint) { 5.70 + try { 5.71 + return requirePrimitive(AbstractJSObject.getDefaultValue(jsobj, hint)); 5.72 + } catch (final UnsupportedOperationException e) { 5.73 + throw new ECMAException(Context.getGlobal().newTypeError(e.getMessage()), e); 5.74 + } 5.75 } 5.76 5.77 /** 5.78 @@ -725,6 +756,18 @@ 5.79 5.80 5.81 /** 5.82 + * JavaScript compliant conversion of Boolean to number 5.83 + * See ECMA 9.3 ToNumber 5.84 + * 5.85 + * @param b a boolean 5.86 + * 5.87 + * @return JS numeric value of the boolean: 1.0 or 0.0 5.88 + */ 5.89 + public static double toNumber(final Boolean b) { 5.90 + return b ? 1d : +0d; 5.91 + } 5.92 + 5.93 + /** 5.94 * JavaScript compliant conversion of Object to number 5.95 * See ECMA 9.3 ToNumber 5.96 * 5.97 @@ -1301,6 +1344,10 @@ 5.98 return (String)obj; 5.99 } 5.100 5.101 + if (obj instanceof ConsString) { 5.102 + return obj.toString(); 5.103 + } 5.104 + 5.105 if (obj instanceof Number) { 5.106 return toString(((Number)obj).doubleValue()); 5.107 } 5.108 @@ -1313,23 +1360,19 @@ 5.109 return "null"; 5.110 } 5.111 5.112 - if (obj instanceof ScriptObject) { 5.113 - if (safe) { 5.114 - final ScriptObject sobj = (ScriptObject)obj; 5.115 - final Global gobj = Context.getGlobal(); 5.116 - return gobj.isError(sobj) ? 5.117 - ECMAException.safeToString(sobj) : 5.118 - sobj.safeToString(); 5.119 - } 5.120 - 5.121 - return toString(toPrimitive(obj, String.class)); 5.122 + if (obj instanceof Boolean) { 5.123 + return obj.toString(); 5.124 } 5.125 5.126 - if (obj instanceof StaticClass) { 5.127 - return "[JavaClass " + ((StaticClass)obj).getRepresentedClass().getName() + "]"; 5.128 + if (safe && obj instanceof ScriptObject) { 5.129 + final ScriptObject sobj = (ScriptObject)obj; 5.130 + final Global gobj = Context.getGlobal(); 5.131 + return gobj.isError(sobj) ? 5.132 + ECMAException.safeToString(sobj) : 5.133 + sobj.safeToString(); 5.134 } 5.135 5.136 - return obj.toString(); 5.137 + return toString(toPrimitive(obj, String.class)); 5.138 } 5.139 5.140 // trim from left for JS whitespaces. 5.141 @@ -1822,18 +1865,18 @@ 5.142 } 5.143 5.144 if (obj instanceof Boolean) { 5.145 - return (Boolean)obj ? 1 : +0.0; 5.146 + return toNumber((Boolean)obj); 5.147 } 5.148 5.149 if (obj instanceof ScriptObject) { 5.150 return toNumber((ScriptObject)obj); 5.151 } 5.152 5.153 - if (obj instanceof JSObject) { 5.154 - return ((JSObject)obj).toNumber(); 5.155 + if (obj instanceof Undefined) { 5.156 + return Double.NaN; 5.157 } 5.158 5.159 - return Double.NaN; 5.160 + return toNumber(toPrimitive(obj, Number.class)); 5.161 } 5.162 5.163 private static Object invoke(final MethodHandle mh, final Object arg) {
6.1 --- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Mon Mar 09 11:34:48 2015 +0100 6.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java Fri Feb 20 15:47:28 2015 +0100 6.3 @@ -32,6 +32,7 @@ 6.4 import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError; 6.5 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 6.6 import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt; 6.7 + 6.8 import java.lang.invoke.MethodHandle; 6.9 import java.lang.invoke.MethodHandles; 6.10 import java.lang.invoke.SwitchPoint; 6.11 @@ -734,7 +735,7 @@ 6.12 return true; 6.13 } 6.14 if (x instanceof ScriptObject && y instanceof ScriptObject) { 6.15 - return x == y; 6.16 + return false; // x != y 6.17 } 6.18 if (x instanceof ScriptObjectMirror || y instanceof ScriptObjectMirror) { 6.19 return ScriptObjectMirror.identical(x, y); 6.20 @@ -798,37 +799,55 @@ 6.21 * @return true if they're equal 6.22 */ 6.23 private static boolean equalDifferentTypeValues(final Object x, final Object y, final JSType xType, final JSType yType) { 6.24 - if (xType == JSType.UNDEFINED && yType == JSType.NULL || xType == JSType.NULL && yType == JSType.UNDEFINED) { 6.25 + if (isUndefinedAndNull(xType, yType) || isUndefinedAndNull(yType, xType)) { 6.26 return true; 6.27 - } 6.28 - 6.29 - if (xType == JSType.NUMBER && yType == JSType.STRING) { 6.30 - return equals(x, JSType.toNumber(y)); 6.31 - } 6.32 - 6.33 - if (xType == JSType.STRING && yType == JSType.NUMBER) { 6.34 - return equals(JSType.toNumber(x), y); 6.35 - } 6.36 - 6.37 - if (xType == JSType.BOOLEAN) { 6.38 - return equals(JSType.toNumber(x), y); 6.39 - } 6.40 - 6.41 - if (yType == JSType.BOOLEAN) { 6.42 - return equals(x, JSType.toNumber(y)); 6.43 - } 6.44 - 6.45 - if ((xType == JSType.STRING || xType == JSType.NUMBER) && y instanceof ScriptObject) { 6.46 - return equals(x, JSType.toPrimitive(y)); 6.47 - } 6.48 - 6.49 - if (x instanceof ScriptObject && (yType == JSType.STRING || yType == JSType.NUMBER)) { 6.50 - return equals(JSType.toPrimitive(x), y); 6.51 + } else if (isNumberAndString(xType, yType)) { 6.52 + return equalNumberToString(x, y); 6.53 + } else if (isNumberAndString(yType, xType)) { 6.54 + // Can reverse order as both are primitives 6.55 + return equalNumberToString(y, x); 6.56 + } else if (xType == JSType.BOOLEAN) { 6.57 + return equalBooleanToAny(x, y); 6.58 + } else if (yType == JSType.BOOLEAN) { 6.59 + // Can reverse order as y is primitive 6.60 + return equalBooleanToAny(y, x); 6.61 + } else if (isNumberOrStringAndObject(xType, yType)) { 6.62 + return equalNumberOrStringToObject(x, y); 6.63 + } else if (isNumberOrStringAndObject(yType, xType)) { 6.64 + // Can reverse order as y is primitive 6.65 + return equalNumberOrStringToObject(y, x); 6.66 } 6.67 6.68 return false; 6.69 } 6.70 6.71 + private static boolean isUndefinedAndNull(final JSType xType, final JSType yType) { 6.72 + return xType == JSType.UNDEFINED && yType == JSType.NULL; 6.73 + } 6.74 + 6.75 + private static boolean isNumberAndString(final JSType xType, final JSType yType) { 6.76 + return xType == JSType.NUMBER && yType == JSType.STRING; 6.77 + } 6.78 + 6.79 + private static boolean isNumberOrStringAndObject(final JSType xType, final JSType yType) { 6.80 + return (xType == JSType.NUMBER || xType == JSType.STRING) && yType == JSType.OBJECT; 6.81 + } 6.82 + 6.83 + private static boolean equalNumberToString(final Object num, final Object str) { 6.84 + // Specification says comparing a number to string should be done as "equals(num, JSType.toNumber(str))". We 6.85 + // can short circuit it to this as we know that "num" is a number, so it'll end up being a number-number 6.86 + // comparison. 6.87 + return ((Number)num).doubleValue() == JSType.toNumber(str.toString()); 6.88 + } 6.89 + 6.90 + private static boolean equalBooleanToAny(final Object bool, final Object any) { 6.91 + return equals(JSType.toNumber((Boolean)bool), any); 6.92 + } 6.93 + 6.94 + private static boolean equalNumberOrStringToObject(final Object numOrStr, final Object any) { 6.95 + return equals(numOrStr, JSType.toPrimitive(any)); 6.96 + } 6.97 + 6.98 /** 6.99 * ECMA 11.9.4 - The strict equal operator (===) - generic implementation 6.100 *
7.1 --- a/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Mon Mar 09 11:34:48 2015 +0100 7.2 +++ b/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java Fri Feb 20 15:47:28 2015 +0100 7.3 @@ -27,14 +27,10 @@ 7.4 7.5 import java.lang.invoke.MethodHandle; 7.6 import java.lang.invoke.MethodHandles; 7.7 -import java.lang.invoke.MethodType; 7.8 -import java.util.HashMap; 7.9 import java.util.Map; 7.10 import javax.script.Bindings; 7.11 import jdk.internal.dynalink.CallSiteDescriptor; 7.12 import jdk.internal.dynalink.linker.GuardedInvocation; 7.13 -import jdk.internal.dynalink.linker.GuardedTypeConversion; 7.14 -import jdk.internal.dynalink.linker.GuardingTypeConverterFactory; 7.15 import jdk.internal.dynalink.linker.LinkRequest; 7.16 import jdk.internal.dynalink.linker.LinkerServices; 7.17 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; 7.18 @@ -49,7 +45,7 @@ 7.19 * A Dynalink linker to handle web browser built-in JS (DOM etc.) objects as well 7.20 * as ScriptObjects from other Nashorn contexts. 7.21 */ 7.22 -final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory { 7.23 +final class JSObjectLinker implements TypeBasedGuardingDynamicLinker { 7.24 private final NashornBeansLinker nashornBeansLinker; 7.25 7.26 JSObjectLinker(final NashornBeansLinker nashornBeansLinker) { 7.27 @@ -95,22 +91,6 @@ 7.28 return Bootstrap.asTypeSafeReturn(inv, linkerServices, desc); 7.29 } 7.30 7.31 - @Override 7.32 - public GuardedTypeConversion convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception { 7.33 - final boolean sourceIsAlwaysJSObject = JSObject.class.isAssignableFrom(sourceType); 7.34 - if(!sourceIsAlwaysJSObject && !sourceType.isAssignableFrom(JSObject.class)) { 7.35 - return null; 7.36 - } 7.37 - 7.38 - final MethodHandle converter = CONVERTERS.get(targetType); 7.39 - if(converter == null) { 7.40 - return null; 7.41 - } 7.42 - 7.43 - return new GuardedTypeConversion(new GuardedInvocation(converter, sourceIsAlwaysJSObject ? null : IS_JSOBJECT_GUARD).asType(MethodType.methodType(targetType, sourceType)), true); 7.44 - } 7.45 - 7.46 - 7.47 private GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request, final LinkerServices linkerServices) throws Exception { 7.48 final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); 7.49 final int c = desc.getNameTokenCount(); 7.50 @@ -208,25 +188,6 @@ 7.51 } 7.52 } 7.53 7.54 - @SuppressWarnings("unused") 7.55 - private static int toInt32(final JSObject obj) { 7.56 - return JSType.toInt32(toNumber(obj)); 7.57 - } 7.58 - 7.59 - @SuppressWarnings("unused") 7.60 - private static long toLong(final JSObject obj) { 7.61 - return JSType.toLong(toNumber(obj)); 7.62 - } 7.63 - 7.64 - private static double toNumber(final JSObject obj) { 7.65 - return obj == null ? 0 : obj.toNumber(); 7.66 - } 7.67 - 7.68 - @SuppressWarnings("unused") 7.69 - private static boolean toBoolean(final JSObject obj) { 7.70 - return obj != null; 7.71 - } 7.72 - 7.73 private static int getIndex(final Number n) { 7.74 final double value = n.doubleValue(); 7.75 return JSType.isRepresentableAsInt(value) ? (int)value : -1; 7.76 @@ -261,14 +222,6 @@ 7.77 private static final MethodHandle JSOBJECT_CALL_TO_APPLY = findOwnMH_S("callToApply", Object.class, MethodHandle.class, JSObject.class, Object.class, Object[].class); 7.78 private static final MethodHandle JSOBJECT_NEW = findJSObjectMH_V("newObject", Object.class, Object[].class); 7.79 7.80 - private static final Map<Class<?>, MethodHandle> CONVERTERS = new HashMap<>(); 7.81 - static { 7.82 - CONVERTERS.put(boolean.class, findOwnMH_S("toBoolean", boolean.class, JSObject.class)); 7.83 - CONVERTERS.put(int.class, findOwnMH_S("toInt32", int.class, JSObject.class)); 7.84 - CONVERTERS.put(long.class, findOwnMH_S("toLong", long.class, JSObject.class)); 7.85 - CONVERTERS.put(double.class, findOwnMH_S("toNumber", double.class, JSObject.class)); 7.86 - } 7.87 - 7.88 private static MethodHandle findJSObjectMH_V(final String name, final Class<?> rtype, final Class<?>... types) { 7.89 return MH.findVirtual(MethodHandles.lookup(), JSObject.class, name, MH.type(rtype, types)); 7.90 }
8.1 --- a/test/script/basic/JDK-8023026.js.EXPECTED Mon Mar 09 11:34:48 2015 +0100 8.2 +++ b/test/script/basic/JDK-8023026.js.EXPECTED Fri Feb 20 15:47:28 2015 +0100 8.3 @@ -26,7 +26,7 @@ 8.4 reduceRight 15 1 8.5 right sum 16 8.6 squared 1,9,25,49 8.7 -iterating on [object Array] 8.8 +iterating on 2,4,6,8 8.9 forEach 2 8.10 forEach 4 8.11 forEach 6
9.1 --- a/test/script/basic/JDK-8024847.js Mon Mar 09 11:34:48 2015 +0100 9.2 +++ b/test/script/basic/JDK-8024847.js Fri Feb 20 15:47:28 2015 +0100 9.3 @@ -102,7 +102,18 @@ 9.4 print(jlist); 9.5 9.6 var obj = new JSObject() { 9.7 - toNumber: function() { return 42; } 9.8 + getMember: function(name) { 9.9 + if (name == "valueOf") { 9.10 + return new JSObject() { 9.11 + isFunction: function() { 9.12 + return true; 9.13 + }, 9.14 + call: function(thiz) { 9.15 + return 42; 9.16 + } 9.17 + }; 9.18 + } 9.19 + } 9.20 }; 9.21 9.22 print(32 + obj);
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/test/script/basic/JDK-8072426.js Fri Feb 20 15:47:28 2015 +0100 10.3 @@ -0,0 +1,164 @@ 10.4 +/* 10.5 + * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. 10.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10.7 + * 10.8 + * This code is free software; you can redistribute it and/or modify it 10.9 + * under the terms of the GNU General Public License version 2 only, as 10.10 + * published by the Free Software Foundation. 10.11 + * 10.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 10.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 10.15 + * version 2 for more details (a copy is included in the LICENSE file that 10.16 + * accompanied this code). 10.17 + * 10.18 + * You should have received a copy of the GNU General Public License version 10.19 + * 2 along with this work; if not, write to the Free Software Foundation, 10.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 10.21 + * 10.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 10.23 + * or visit www.oracle.com if you need additional information or have any 10.24 + * questions. 10.25 + */ 10.26 + 10.27 +/** 10.28 + * JDK-8072426: Can't compare Java objects to strings or numbers 10.29 + * 10.30 + * @test 10.31 + * @run 10.32 + */ 10.33 + 10.34 +Assert.assertTrue(java.math.RoundingMode.UP == "UP"); 10.35 + 10.36 +var JSObject = Java.type("jdk.nashorn.api.scripting.JSObject"); 10.37 + 10.38 +// Adds an "isFunction" member to the JSObject that returns the specified value 10.39 +function addIsFunction(isFunction, obj) { 10.40 + obj.isFunction = function() { 10.41 + return isFunction; 10.42 + }; 10.43 + return obj; 10.44 +} 10.45 + 10.46 +function makeJSObjectConstantFunction(value) { 10.47 + return new JSObject(addIsFunction(true, { 10.48 + call: function() { 10.49 + return value; 10.50 + } 10.51 + })); 10.52 +} 10.53 + 10.54 +function makeJSObjectWithMembers(mapping) { 10.55 + return new JSObject({ 10.56 + getMember: function(name) { 10.57 + Assert.assertTrue(mapping.hasOwnProperty(name)); 10.58 + return mapping[name]; 10.59 + }, 10.60 + toNumber: function() { 10.61 + // toNumber no longer invoked 10.62 + Assert.fail(); 10.63 + } 10.64 + }); 10.65 +} 10.66 + 10.67 +// Test JSObjectLinker toInt32/toLong/toNumber 10.68 +function testNumericJSObject(kind, value) { 10.69 + var obj = makeJSObjectWithMembers({ 10.70 + valueOf: makeJSObjectConstantFunction(value) 10.71 + }); 10.72 + 10.73 + if (kind === "double") { 10.74 + // There's no assertEquals(double actual, double expected). There's only 10.75 + // assertEquals(double actual, double expected, double delta). 10.76 + Assert["assertEquals(double,double,double)"](value, obj, 0); 10.77 + } else { 10.78 + Assert["assertEquals(" + kind + ", " + kind + ")"](value, obj); 10.79 + } 10.80 + Assert.assertTrue(value == Number(obj)); 10.81 +} 10.82 +testNumericJSObject("int", 42); 10.83 +testNumericJSObject("long", 4294967296); 10.84 +testNumericJSObject("double", 1.2); 10.85 + 10.86 +// Test fallback from toNumber to toString for numeric conversion when toNumber doesn't exist 10.87 +(function() { 10.88 + var obj = makeJSObjectWithMembers({ 10.89 + valueOf: null, // Explicitly no valueOf 10.90 + toString: makeJSObjectConstantFunction("123") 10.91 + }); 10.92 + Assert["assertEquals(int,int)"](123, obj); 10.93 +})(); 10.94 + 10.95 +// Test fallback from toNumber to toString for numeric conversion when toNumber isn't a callable 10.96 +(function() { 10.97 + var obj = makeJSObjectWithMembers({ 10.98 + valueOf: new JSObject(addIsFunction(false, {})), 10.99 + toString: makeJSObjectConstantFunction("124") 10.100 + }); 10.101 + Assert["assertEquals(int,int)"](124, obj); 10.102 +})(); 10.103 + 10.104 +// Test fallback from toNumber to toString for numeric conversion when toNumber returns a non-primitive 10.105 +(function() { 10.106 + var obj = makeJSObjectWithMembers({ 10.107 + valueOf: makeJSObjectConstantFunction({}), 10.108 + toString: makeJSObjectConstantFunction("125") 10.109 + }); 10.110 + Assert["assertEquals(int,int)"](125, obj); 10.111 +})(); 10.112 + 10.113 +// Test TypeError from toNumber to toString when both return a non-primitive 10.114 +(function() { 10.115 + var obj = makeJSObjectWithMembers({ 10.116 + valueOf: makeJSObjectConstantFunction({}), 10.117 + toString: makeJSObjectConstantFunction({}) 10.118 + }); 10.119 + try { 10.120 + Number(obj); 10.121 + Assert.fail(); // must throw 10.122 + } catch(e) { 10.123 + Assert.assertTrue(e instanceof TypeError); 10.124 + } 10.125 +})(); 10.126 + 10.127 +// Test toString for string conversion 10.128 +(function() { 10.129 + var obj = makeJSObjectWithMembers({ 10.130 + toString: makeJSObjectConstantFunction("Hello") 10.131 + }); 10.132 + Assert.assertTrue("Hello" === String(obj)); 10.133 + Assert["assertEquals(String,String)"]("Hello", obj); 10.134 +})(); 10.135 + 10.136 +// Test fallback from toString to valueOf for string conversion when toString doesn't exist 10.137 +(function() { 10.138 + var obj = makeJSObjectWithMembers({ 10.139 + toString: null, 10.140 + valueOf: makeJSObjectConstantFunction("Hello1") 10.141 + }); 10.142 + Assert.assertTrue("Hello1" === String(obj)); 10.143 + Assert["assertEquals(String,String)"]("Hello1", obj); 10.144 +})(); 10.145 + 10.146 +// Test fallback from toString to valueOf for string conversion when toString is not callable 10.147 +(function() { 10.148 + var obj = makeJSObjectWithMembers({ 10.149 + toString: new JSObject(addIsFunction(false, {})), 10.150 + valueOf: makeJSObjectConstantFunction("Hello2") 10.151 + }); 10.152 + Assert["assertEquals(String,String)"]("Hello2", obj); 10.153 +})(); 10.154 + 10.155 +// Test fallback from toString to valueOf for string conversion when toString returns non-primitive 10.156 +(function() { 10.157 + var obj = makeJSObjectWithMembers({ 10.158 + toString: makeJSObjectConstantFunction({}), 10.159 + valueOf: makeJSObjectConstantFunction("Hello3") 10.160 + }); 10.161 + Assert["assertEquals(String,String)"]("Hello3", obj); 10.162 +})(); 10.163 + 10.164 +// Test toBoolean for JSObject 10.165 +(function() { 10.166 + Assert["assertEquals(boolean,boolean)"](true, new JSObject({})); 10.167 +})();