8027031: complete merging of loads and converts
authorattila
Tue, 22 Oct 2013 16:43:27 +0200
changeset 21449 72d51df5ed85
parent 21447 5efe5ca7b352
child 21450 419e5d51f319
8027031: complete merging of loads and converts Reviewed-by: jlaskey, lagergren
nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java
nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java
nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java
nashorn/src/jdk/nashorn/internal/runtime/JSType.java
nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java
--- a/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Tue Oct 22 17:38:12 2013 +0530
+++ b/nashorn/src/jdk/nashorn/api/scripting/ScriptObjectMirror.java	Tue Oct 22 16:43:27 2013 +0200
@@ -43,6 +43,7 @@
 import javax.script.Bindings;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.GlobalObject;
+import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.ScriptFunction;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.ScriptRuntime;
@@ -705,4 +706,45 @@
             }
         }
     }
+
+    /**
+     * JavaScript compliant Object to int32 conversion
+     * See ECMA 9.5 ToInt32
+     *
+     * @return this object's int32 representation
+     */
+    public int toInt32() {
+        return inGlobal(new Callable<Integer>() {
+            @Override public Integer call() {
+                return JSType.toInt32(sobj);
+            }
+        });
+    }
+
+    /**
+     * JavaScript compliant Object to int64 conversion
+     *
+     * @return this object's int64 representation
+     */
+    public long toInt64() {
+        return inGlobal(new Callable<Long>() {
+            @Override public Long call() {
+                return JSType.toInt64(sobj);
+            }
+        });
+    }
+
+    /**
+     * JavaScript compliant conversion of Object to number
+     * See ECMA 9.3 ToNumber
+     *
+     * @return this object's number representation
+     */
+    public double toNumber() {
+        return inGlobal(new Callable<Double>() {
+            @Override public Double call() {
+                return JSType.toNumber(sobj);
+            }
+        });
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Tue Oct 22 17:38:12 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/BranchOptimizer.java	Tue Oct 22 16:43:27 2013 +0200
@@ -72,8 +72,7 @@
         }
 
         // convert to boolean
-        codegen.load(unaryNode);
-        method.convert(Type.BOOLEAN);
+        codegen.load(unaryNode, Type.BOOLEAN);
         if (state) {
             method.ifne(label);
         } else {
@@ -146,8 +145,7 @@
             break;
         }
 
-        codegen.load(binaryNode);
-        method.convert(Type.BOOLEAN);
+        codegen.load(binaryNode, Type.BOOLEAN);
         if (state) {
             method.ifne(label);
         } else {
@@ -169,8 +167,7 @@
             }
         }
 
-        codegen.load(node);
-        method.convert(Type.BOOLEAN);
+        codegen.load(node, Type.BOOLEAN);
         if (state) {
             method.ifne(label);
         } else {
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Oct 22 17:38:12 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Oct 22 16:43:27 2013 +0200
@@ -402,7 +402,7 @@
         return loadBinaryOperands(node.lhs(), node.rhs(), node.getType(), false);
     }
 
-    private MethodEmitter load(final Expression node, final Type type) {
+    MethodEmitter load(final Expression node, final Type type) {
         return load(node, type, false);
     }
 
@@ -432,7 +432,7 @@
             @Override
             public boolean enterAccessNode(final AccessNode accessNode) {
                 if (!baseAlreadyOnStack) {
-                    load(accessNode.getBase()).convert(Type.OBJECT);
+                    load(accessNode.getBase(), Type.OBJECT);
                 }
                 assert method.peekType().isObject();
                 method.dynamicGet(type, accessNode.getProperty().getName(), getCallSiteFlags(), accessNode.isFunction());
@@ -442,7 +442,7 @@
             @Override
             public boolean enterIndexNode(final IndexNode indexNode) {
                 if (!baseAlreadyOnStack) {
-                    load(indexNode.getBase()).convert(Type.OBJECT);
+                    load(indexNode.getBase(), Type.OBJECT);
                     load(indexNode.getIndex());
                 }
                 method.dynamicGetIndex(type, getCallSiteFlags(), indexNode.isFunction());
@@ -632,11 +632,13 @@
         final Type[] params = signature == null ? null : Type.getMethodArguments(signature);
         for (final Expression arg : args) {
             assert arg != null;
-            load(arg);
             if (n >= argCount) {
+                load(arg);
                 method.pop(); // we had to load the arg for its side effects
             } else if (params != null) {
-                method.convert(params[n]);
+                load(arg, params[n]);
+            } else {
+                load(arg);
             }
             n++;
         }
@@ -1277,7 +1279,7 @@
         for (int i = 0; i < args.size(); i++) {
             method.dup();
             method.load(i);
-            load(args.get(i)).convert(Type.OBJECT); //has to be upcast to object or we fail
+            load(args.get(i), Type.OBJECT); //has to be upcast to object or we fail
             method.arraystore();
         }
 
@@ -1719,7 +1721,7 @@
         }
 
         for (final Expression arg : args) {
-            load(arg).convert(Type.OBJECT);
+            load(arg, Type.OBJECT);
         }
 
         method.invokestatic(
@@ -2105,7 +2107,7 @@
 
             if (exceptionCondition != null) {
                 next = new Label("next");
-                load(exceptionCondition).convert(Type.BOOLEAN).ifeq(next);
+                load(exceptionCondition, Type.BOOLEAN).ifeq(next);
             } else {
                 next = null;
             }
@@ -2352,7 +2354,7 @@
         final List<Expression> args   = callNode.getArgs();
 
         // Load function reference.
-        load(callNode.getFunction()).convert(Type.OBJECT); // must detect type error
+        load(callNode.getFunction(), Type.OBJECT); // must detect type error
 
         method.dynamicNew(1 + loadArgs(args), getCallSiteFlags());
         method.store(unaryNode.getSymbol());
@@ -2383,7 +2385,7 @@
     @Override
     public boolean enterSUB(final UnaryNode unaryNode) {
         assert unaryNode.getType().isNumeric();
-        load(unaryNode.rhs()).convert(unaryNode.getType()).neg().store(unaryNode.getSymbol());
+        load(unaryNode.rhs(), unaryNode.getType()).neg().store(unaryNode.getSymbol());
         return false;
     }
 
@@ -2424,7 +2426,7 @@
 
         final Label skip = new Label("skip");
 
-        load(lhs).convert(Type.OBJECT).dup().convert(Type.BOOLEAN);
+        load(lhs, Type.OBJECT).dup().convert(Type.BOOLEAN);
 
         if (binaryNode.tokenType() == TokenType.AND) {
             method.ifeq(skip);
@@ -2433,7 +2435,7 @@
         }
 
         method.pop();
-        load(rhs).convert(Type.OBJECT);
+        load(rhs, Type.OBJECT);
         method.label(skip);
         method.store(binaryNode.getSymbol());
 
--- a/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Tue Oct 22 17:38:12 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/SpillObjectCreator.java	Tue Oct 22 16:43:27 2013 +0200
@@ -32,7 +32,6 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
-
 import jdk.nashorn.internal.codegen.types.Type;
 import jdk.nashorn.internal.ir.Expression;
 import jdk.nashorn.internal.ir.LiteralNode;
@@ -143,7 +142,7 @@
                 method.dup();
                 method.getField(Type.getInternalName(ScriptObject.class), "spill", Type.OBJECT_ARRAY.getDescriptor());
                 method.load(property.getSlot());
-                codegen.load(values.get(i)).convert(OBJECT);
+                codegen.load(values.get(i), OBJECT);
                 method.arraystore();
             }
         }
--- a/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Tue Oct 22 17:38:12 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/JSType.java	Tue Oct 22 16:43:27 2013 +0200
@@ -246,12 +246,11 @@
      * @return the primitive form of the object
      */
     public static Object toPrimitive(final Object obj, final Class<?> hint) {
-        if (!(obj instanceof ScriptObject)) {
-            return obj;
-        }
+        return obj instanceof ScriptObject ? toPrimitive((ScriptObject)obj, hint) : obj;
+    }
 
-        final ScriptObject sobj   = (ScriptObject)obj;
-        final Object       result = sobj.getDefaultValue(hint);
+    private static Object toPrimitive(final ScriptObject sobj, final Class<?> hint) {
+        final Object result = sobj.getDefaultValue(hint);
 
         if (!isPrimitive(result)) {
             throw typeError("bad.default.value", result.toString());
@@ -475,6 +474,19 @@
         return toNumberGeneric(obj);
     }
 
+
+    /**
+     * JavaScript compliant conversion of Object to number
+     * See ECMA 9.3 ToNumber
+     *
+     * @param obj  an object
+     *
+     * @return a number
+     */
+    public static double toNumber(final ScriptObject obj) {
+        return toNumber(toPrimitive(obj, Number.class));
+    }
+
     /**
      * Digit representation for a character
      *
@@ -619,6 +631,17 @@
     }
 
     /**
+     * JavaScript compliant Object to int32 conversion
+     * See ECMA 9.5 ToInt32
+     *
+     * @param obj an object
+     * @return an int32
+     */
+    public static int toInt32(final ScriptObject obj) {
+        return toInt32(toNumber(obj));
+    }
+
+    /**
      * JavaScript compliant long to int32 conversion
      *
      * @param num a long
@@ -649,6 +672,16 @@
     }
 
     /**
+     * JavaScript compliant Object to int64 conversion
+     *
+     * @param obj an object
+     * @return an int64
+     */
+    public static long toInt64(final ScriptObject obj) {
+        return toInt64(toNumber(obj));
+    }
+
+    /**
      * JavaScript compliant number to int64 conversion
      *
      * @param num a number
@@ -1028,7 +1061,7 @@
         }
 
         if (obj instanceof ScriptObject) {
-            return toNumber(toPrimitive(obj, Number.class));
+            return toNumber((ScriptObject)obj);
         }
 
         return Double.NaN;
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Tue Oct 22 17:38:12 2013 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java	Tue Oct 22 16:43:27 2013 +0200
@@ -25,25 +25,29 @@
 
 package jdk.nashorn.internal.runtime.linker;
 
-import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
-import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.invoke.MethodType;
+import java.util.HashMap;
+import java.util.Map;
 import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
+import jdk.internal.dynalink.linker.GuardingTypeConverterFactory;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.internal.dynalink.linker.LinkerServices;
 import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
 import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
+import jdk.nashorn.api.scripting.JSObject;
+import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.lookup.MethodHandleFactory;
+import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
 import jdk.nashorn.internal.runtime.JSType;
-import jdk.nashorn.api.scripting.JSObject;
 
 /**
  * A Dynalink linker to handle web browser built-in JS (DOM etc.) objects as well
  * as ScriptObjects from other Nashorn contexts.
  */
-final class JSObjectLinker implements TypeBasedGuardingDynamicLinker {
+final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTypeConverterFactory {
     @Override
     public boolean canLinkType(final Class<?> type) {
         return canLinkTypeStatic(type);
@@ -75,6 +79,21 @@
         return Bootstrap.asType(inv, linkerServices, desc);
     }
 
+    @Override
+    public GuardedInvocation convertToType(final Class<?> sourceType, final Class<?> targetType) throws Exception {
+        if(!sourceType.isAssignableFrom(ScriptObjectMirror.class)) {
+            return null;
+        }
+
+        final MethodHandle converter = MIRROR_CONVERTERS.get(targetType);
+        if(converter == null) {
+            return null;
+        }
+
+        return new GuardedInvocation(converter, sourceType == ScriptObjectMirror.class ? null : IS_MIRROR_GUARD).asType(MethodType.methodType(targetType, sourceType));
+    }
+
+
     private static GuardedInvocation lookup(final CallSiteDescriptor desc) {
         final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0);
         final int c = desc.getNameTokenCount();
@@ -87,9 +106,9 @@
             case "setElem":
                 return c > 2 ? findSetMethod(desc) : findSetIndexMethod();
             case "call":
-                return findCallMethod(desc, operator);
+                return findCallMethod(desc);
             case "callMethod":
-                return findCallMethodMethod(desc, operator);
+                return findCallMethodMethod(desc);
             case "new":
                 return findNewMethod(desc);
             default:
@@ -115,14 +134,14 @@
         return new GuardedInvocation(JSOBJECTLINKER_PUT, null, IS_JSOBJECT_GUARD);
     }
 
-    private static GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc, final String operator) {
+    private static GuardedInvocation findCallMethodMethod(final CallSiteDescriptor desc) {
         final String methodName = desc.getNameToken(2);
         MethodHandle func = MH.insertArguments(JSOBJECT_CALLMEMBER, 1, methodName);
         func = MH.asCollector(func, Object[].class, desc.getMethodType().parameterCount() - 1);
         return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
     }
 
-    private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc, final String operator) {
+    private static GuardedInvocation findCallMethod(final CallSiteDescriptor desc) {
         final MethodHandle func = MH.asCollector(JSOBJECT_CALL, Object[].class, desc.getMethodType().parameterCount() - 2);
         return new GuardedInvocation(func, null, IS_JSOBJECT_GUARD);
     }
@@ -138,6 +157,11 @@
     }
 
     @SuppressWarnings("unused")
+    private static boolean isScriptObjectMirror(final Object self) {
+        return self instanceof ScriptObjectMirror;
+    }
+
+    @SuppressWarnings("unused")
     private static Object get(final Object jsobj, final Object key) {
         if (key instanceof Integer) {
             return ((JSObject)jsobj).getSlot((Integer)key);
@@ -172,6 +196,7 @@
 
     // method handles of the current class
     private static final MethodHandle IS_JSOBJECT_GUARD  = findOwnMH("isJSObject", boolean.class, Object.class);
+    private static final MethodHandle IS_MIRROR_GUARD    = findOwnMH("isScriptObjectMirror", boolean.class, Object.class);
     private static final MethodHandle JSOBJECTLINKER_GET = findOwnMH("get", Object.class, Object.class, Object.class);
     private static final MethodHandle JSOBJECTLINKER_PUT = findOwnMH("put", Void.TYPE, Object.class, Object.class, Object.class);
 
@@ -182,23 +207,32 @@
     private static final MethodHandle JSOBJECT_CALL       = findJSObjectMH("call", Object.class, Object.class, Object[].class);
     private static final MethodHandle JSOBJECT_NEW        = findJSObjectMH("newObject", Object.class, Object[].class);
 
+    private static final Map<Class<?>, MethodHandle> MIRROR_CONVERTERS = new HashMap<>();
+    static {
+        MIRROR_CONVERTERS.put(boolean.class, MH.dropArguments(MH.constant(boolean.class, Boolean.TRUE), 0, Object.class));
+        MIRROR_CONVERTERS.put(int.class, findMirrorMH("toInt32", int.class));
+        MIRROR_CONVERTERS.put(long.class, findMirrorMH("toInt64", long.class));
+        MIRROR_CONVERTERS.put(double.class, findMirrorMH("toNumber", double.class));
+    }
+
     private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        final Class<?>   own = JSObjectLinker.class;
-        final MethodType mt  = MH.type(rtype, types);
-        try {
-            return MH.findStatic(MethodHandles.lookup(), own, name, mt);
-        } catch (final MethodHandleFactory.LookupException e) {
-            return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
-        }
+        return findMH(name, JSObjectLinker.class, rtype, types);
     }
 
     private static MethodHandle findJSObjectMH(final String name, final Class<?> rtype, final Class<?>... types) {
-        final Class<?>   own = JSObject.class;
+        return findMH(name, JSObject.class, rtype, types);
+    }
+
+    private static MethodHandle findMirrorMH(final String name, final Class<?> rtype, final Class<?>... types) {
+        return findMH(name, ScriptObjectMirror.class, rtype, types);
+    }
+
+    private static MethodHandle findMH(final String name, final Class<?> target, final Class<?> rtype, final Class<?>... types) {
         final MethodType mt  = MH.type(rtype, types);
         try {
-            return MH.findVirtual(MethodHandles.publicLookup(), own, name, mt);
+            return MH.findStatic(MethodHandles.lookup(), target, name, mt);
         } catch (final MethodHandleFactory.LookupException e) {
-            return MH.findVirtual(MethodHandles.lookup(), own, name, mt);
+            return MH.findVirtual(MethodHandles.lookup(), target, name, mt);
         }
     }
 }