8043002: Improve performance of Nashorn equality operators
Reviewed-by: lagergren, sundar
--- 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;
}