8072426: Can't compare Java objects to strings or numbers

Fri, 20 Feb 2015 15:47:28 +0100

author
attila
date
Fri, 20 Feb 2015 15:47:28 +0100
changeset 1250
9ee1fc3f6136
parent 1249
02702b17f1d8
child 1251
85a6a7545dbe

8072426: Can't compare Java objects to strings or numbers
Reviewed-by: hannesw, lagergren, sundar

src/jdk/nashorn/api/scripting/AbstractJSObject.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/api/scripting/DefaultValueImpl.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/api/scripting/JSObject.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/api/scripting/ScriptObjectMirror.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/JSType.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptRuntime.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java file | annotate | diff | comparison | revisions
test/script/basic/JDK-8023026.js.EXPECTED file | annotate | diff | comparison | revisions
test/script/basic/JDK-8024847.js file | annotate | diff | comparison | revisions
test/script/basic/JDK-8072426.js file | annotate | diff | comparison | revisions
     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 +})();

mercurial