8043002: Improve performance of Nashorn equality operators
authorattila
Tue, 13 May 2014 14:54:21 +0200
changeset 24752 c835f368e8e0
parent 24751 ccbd9cd3f720
child 24753 675feda2f82d
8043002: Improve performance of Nashorn equality operators Reviewed-by: lagergren, sundar
nashorn/src/jdk/nashorn/internal/objects/NativeObject.java
nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java
nashorn/src/jdk/nashorn/internal/runtime/JSType.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java	Tue May 13 11:30:40 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeObject.java	Tue May 13 14:54:21 2014 +0200
@@ -38,7 +38,6 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
-
 import jdk.internal.dynalink.beans.BeansLinker;
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.internal.dynalink.linker.GuardedInvocation;
@@ -418,7 +417,7 @@
      */
     @Constructor
     public static Object construct(final boolean newObj, final Object self, final Object value) {
-        final JSType type = JSType.of(value);
+        final JSType type = JSType.ofNoFunction(value);
 
         // Object(null), Object(undefined), Object() are same as "new Object()"
 
@@ -429,7 +428,6 @@
             case STRING:
                 return Global.toObject(value);
             case OBJECT:
-            case FUNCTION:
                 return value;
             case NULL:
             case UNDEFINED:
--- a/nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java	Tue May 13 11:30:40 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/DebuggerSupport.java	Tue May 13 14:54:21 2014 +0200
@@ -183,7 +183,7 @@
             for (long i = 0; i < length; i++) {
                 if (object.has(i)) {
                     final Object valueAsObject = object.get(i);
-                    final boolean isUndefined = JSType.of(valueAsObject) == JSType.UNDEFINED;
+                    final boolean isUndefined = valueAsObject == ScriptRuntime.UNDEFINED;
 
                     if (isUndefined) {
                         if (i != 0) {
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Tue May 13 11:30:40 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Tue May 13 14:54:21 2014 +0200
@@ -310,6 +310,44 @@
     }
 
     /**
+     * Similar to {@link #of(Object)}, but does not distinguish between {@link #FUNCTION} and {@link #OBJECT}, returning
+     * {@link #OBJECT} in both cases. The distinction is costly, and the EQ and STRICT_EQ predicates don't care about it
+     * so we maintain this version for their use.
+     *
+     * @param obj an object
+     *
+     * @return the JSType for the object; returns {@link #OBJECT} instead of {@link #FUNCTION} for functions.
+     */
+    public static JSType ofNoFunction(final Object obj) {
+        // Order of these statements is tuned for performance (see JDK-8024476)
+        if (obj == null) {
+            return JSType.NULL;
+        }
+
+        if (obj instanceof ScriptObject) {
+            return JSType.OBJECT;
+        }
+
+        if (obj instanceof Boolean) {
+            return JSType.BOOLEAN;
+        }
+
+        if (obj instanceof String || obj instanceof ConsString) {
+            return JSType.STRING;
+        }
+
+        if (obj instanceof Number) {
+            return JSType.NUMBER;
+        }
+
+        if (obj == ScriptRuntime.UNDEFINED) {
+            return JSType.UNDEFINED;
+        }
+
+        return JSType.OBJECT;
+    }
+
+    /**
      * Void return method handle glue
      */
     public static void voidReturn() {
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Tue May 13 11:30:40 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptRuntime.java	Tue May 13 14:54:21 2014 +0200
@@ -42,7 +42,6 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-
 import jdk.internal.dynalink.beans.StaticClass;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
@@ -167,7 +166,7 @@
         // But we don't need to -- all we need is the right class name
         // of the corresponding primitive wrapper type.
 
-        final JSType type = JSType.of(self);
+        final JSType type = JSType.ofNoFunction(self);
 
         switch (type) {
         case BOOLEAN:
@@ -187,7 +186,6 @@
             className = "Undefined";
             break;
         case OBJECT:
-        case FUNCTION:
             if (self instanceof ScriptObject) {
                 className = ((ScriptObject)self).getClassName();
             } else if (self instanceof JSObject) {
@@ -407,8 +405,8 @@
      * @return true if both objects have the same value
      */
     public static boolean sameValue(final Object x, final Object y) {
-        final JSType xType = JSType.of(x);
-        final JSType yType = JSType.of(y);
+        final JSType xType = JSType.ofNoFunction(x);
+        final JSType yType = JSType.ofNoFunction(y);
 
         if (xType != yType) {
             return false;
@@ -681,8 +679,8 @@
 
     /** ECMA 11.9.3 The Abstract Equality Comparison Algorithm */
     private static boolean equals(final Object x, final Object y) {
-        final JSType xType = JSType.of(x);
-        final JSType yType = JSType.of(y);
+        final JSType xType = JSType.ofNoFunction(x);
+        final JSType yType = JSType.ofNoFunction(y);
 
         if (xType == yType) {
 
@@ -691,13 +689,7 @@
             }
 
             if (xType == JSType.NUMBER) {
-                final double xVal = ((Number)x).doubleValue();
-                final double yVal = ((Number)y).doubleValue();
-                if (Double.isNaN(xVal) || Double.isNaN(yVal)) {
-                    return false;
-                }
-
-                return xVal == yVal;
+                return ((Number)x).doubleValue() == ((Number)y).doubleValue();
             }
 
             if (xType == JSType.STRING) {
@@ -706,8 +698,7 @@
             }
 
             if (xType == JSType.BOOLEAN) {
-                // Boolean comparison
-                return x.equals(y);
+                return ((Boolean)x).booleanValue() == ((Boolean)y).booleanValue();
             }
 
             return x == y;
@@ -773,8 +764,8 @@
 
     /** ECMA 11.9.6 The Strict Equality Comparison Algorithm */
     private static boolean strictEquals(final Object x, final Object y) {
-        final JSType xType = JSType.of(x);
-        final JSType yType = JSType.of(y);
+        final JSType xType = JSType.ofNoFunction(x);
+        final JSType yType = JSType.ofNoFunction(y);
 
         if (xType != yType) {
             return false;
@@ -785,14 +776,7 @@
         }
 
         if (xType == JSType.NUMBER) {
-            final double xVal = ((Number)x).doubleValue();
-            final double yVal = ((Number)y).doubleValue();
-
-            if (Double.isNaN(xVal) || Double.isNaN(yVal)) {
-                return false;
-            }
-
-            return xVal == yVal;
+            return ((Number)x).doubleValue() == ((Number)y).doubleValue();
         }
 
         if (xType == JSType.STRING) {
@@ -801,7 +785,7 @@
         }
 
         if (xType == JSType.BOOLEAN) {
-            return x.equals(y);
+            return ((Boolean)x).booleanValue() == ((Boolean)y).booleanValue();
         }
 
         // finally, the object identity comparison
@@ -817,9 +801,9 @@
      * @return true if objects are equal
      */
     public static boolean IN(final Object property, final Object obj) {
-        final JSType rvalType = JSType.of(obj);
+        final JSType rvalType = JSType.ofNoFunction(obj);
 
-        if (rvalType == JSType.OBJECT || rvalType == JSType.FUNCTION) {
+        if (rvalType == JSType.OBJECT) {
             if (obj instanceof ScriptObject) {
                 return ((ScriptObject)obj).has(property);
             }
@@ -931,7 +915,7 @@
             px = JSType.toPrimitive(x, Number.class);
         }
 
-        if (JSType.of(px) == JSType.STRING && JSType.of(py) == JSType.STRING) {
+        if (JSType.ofNoFunction(px) == JSType.STRING && JSType.ofNoFunction(py) == JSType.STRING) {
             // May be String or ConsString
             return px.toString().compareTo(py.toString()) < 0;
         }