Merge
authorjlaskey
Tue, 29 Oct 2013 10:40:00 -0300
changeset 21459 5b10d6333fef
parent 21456 cadbbc8e6068 (current diff)
parent 21458 eec7878b0dcd (diff)
child 21463 351ccd3f56e3
Merge
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java	Tue Oct 29 10:40:00 2013 -0300
@@ -359,8 +359,11 @@
         return load(node, node.hasType() ? node.getType() : null, false);
     }
 
-    private static boolean safeLiteral(final Expression rhs) {
-        return rhs instanceof LiteralNode && !(rhs instanceof ArrayLiteralNode);
+    // Test whether conversion from source to target involves a call of ES 9.1 ToPrimitive
+    // with possible side effects from calling an object's toString or valueOf methods.
+    private boolean noToPrimitiveConversion(final Type source, final Type target) {
+        // Object to boolean conversion does not cause ToPrimitive call
+        return source.isJSPrimitive() || !target.isJSPrimitive() || target.isBoolean();
     }
 
     MethodEmitter loadBinaryOperands(final Expression lhs, final Expression rhs, final Type type) {
@@ -374,25 +377,19 @@
         // can combine a LOAD with a CONVERT operation (e.g. use a dynamic getter with the conversion target type as its
         // return value). What we do here is reorder LOAD RIGHT and CONVERT LEFT when possible; it is possible only when
         // we can prove that executing CONVERT LEFT can't have a side effect that changes the value of LOAD RIGHT.
-        // Basically, if we know that either LEFT is not an object, or RIGHT is a constant literal, then we can do the
+        // Basically, if we know that either LEFT already is a primitive value, or does not have to be converted to
+        // a primitive value, or RIGHT is an expression that loads without side effects, then we can do the
         // reordering and collapse LOAD/CONVERT into a single operation; otherwise we need to do the more costly
         // separate operations to preserve specification semantics.
-        final Type lhsType = lhs.getType();
-        if (lhsType.isObject() && !safeLiteral(rhs)) {
-            // Can't reorder. Load and convert separately.
-            load(lhs, lhsType, baseAlreadyOnStack);
-            load(rhs, rhs.getType(), false);
-            // Avoid empty SWAP, SWAP bytecode sequence if CONVERT LEFT is a no-op
-            if (!lhsType.isEquivalentTo(type)) {
-                method.swap();
-                method.convert(type);
-                method.swap();
-            }
-            method.convert(type);
-        } else {
+        if (noToPrimitiveConversion(lhs.getType(), type) || rhs.isLocal()) {
             // Can reorder. Combine load and convert into single operations.
             load(lhs, type, baseAlreadyOnStack);
             load(rhs, type, false);
+        } else {
+            // Can't reorder. Load and convert separately.
+            load(lhs, lhs.getType(), baseAlreadyOnStack);
+            load(rhs, rhs.getType(), false);
+            method.swap().convert(type).swap().convert(type);
         }
 
         return method;
--- a/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/codegen/types/Type.java	Tue Oct 29 10:40:00 2013 -0300
@@ -292,6 +292,16 @@
     }
 
     /**
+     * Determines whether this type represents an primitive type according to the ECMAScript specification,
+     * which includes Boolean, Number, and String.
+     *
+     * @return true if a JavaScript primitive type, false otherwise.
+     */
+    public boolean isJSPrimitive() {
+        return !isObject() || isString();
+    }
+
+    /**
      * Determines whether a type is the BOOLEAN type
      * @return true if BOOLEAN, false otherwise
      */
@@ -443,7 +453,7 @@
         } else if (type0.isArray() != type1.isArray()) {
             //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense
             return Type.OBJECT;
-        } else if (type0.isObject() && type1.isObject() && ((ObjectType)type0).getTypeClass() != ((ObjectType)type1).getTypeClass()) {
+        } else if (type0.isObject() && type1.isObject() && type0.getTypeClass() != type1.getTypeClass()) {
             // Object<type=String> and Object<type=ScriptFunction> will produce Object
             // TODO: maybe find most specific common superclass?
             return Type.OBJECT;
--- a/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/ir/BinaryNode.java	Tue Oct 29 10:40:00 2013 -0300
@@ -90,6 +90,9 @@
             return Type.LONG;
         case ASSIGN_SAR:
         case ASSIGN_SHL:
+        case BIT_AND:
+        case BIT_OR:
+        case BIT_XOR:
         case ASSIGN_BIT_AND:
         case ASSIGN_BIT_OR:
         case ASSIGN_BIT_XOR:
@@ -170,6 +173,42 @@
     }
 
     @Override
+    public boolean isLocal() {
+        switch (tokenType()) {
+        case SAR:
+        case SHL:
+        case SHR:
+        case BIT_AND:
+        case BIT_OR:
+        case BIT_XOR:
+        case ADD:
+        case DIV:
+        case MOD:
+        case MUL:
+        case SUB:
+            return lhs.isLocal() && lhs.getType().isJSPrimitive()
+                && rhs.isLocal() && rhs.getType().isJSPrimitive();
+        case ASSIGN_ADD:
+        case ASSIGN_BIT_AND:
+        case ASSIGN_BIT_OR:
+        case ASSIGN_BIT_XOR:
+        case ASSIGN_DIV:
+        case ASSIGN_MOD:
+        case ASSIGN_MUL:
+        case ASSIGN_SAR:
+        case ASSIGN_SHL:
+        case ASSIGN_SHR:
+        case ASSIGN_SUB:
+            return lhs instanceof IdentNode && lhs.isLocal() && lhs.getType().isJSPrimitive()
+                    && rhs.isLocal() && rhs.getType().isJSPrimitive();
+        case ASSIGN:
+            return lhs instanceof IdentNode && lhs.isLocal() && rhs.isLocal();
+        default:
+            return false;
+        }
+    }
+
+    @Override
     public void toString(final StringBuilder sb) {
         final TokenType type = tokenType();
 
--- a/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/ir/Expression.java	Tue Oct 29 10:40:00 2013 -0300
@@ -96,4 +96,16 @@
         assert hasType() : this + " has no type";
         return symbol.getSymbolType();
     }
+
+    /**
+     * Returns {@code true} if this expression depends exclusively on state that is constant
+     * or local to the currently running function and thus inaccessible to other functions.
+     * This implies that a local expression must not call any other functions (neither directly
+     * nor implicitly through a getter, setter, or object-to-primitive type conversion).
+     *
+     * @return true if this expression does not depend on state shared with other functions.
+     */
+    public boolean isLocal() {
+        return false;
+    }
 }
--- a/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/ir/IdentNode.java	Tue Oct 29 10:40:00 2013 -0300
@@ -138,6 +138,11 @@
         return getName();
     }
 
+    @Override
+    public boolean isLocal() {
+        return !getSymbol().isScope();
+    }
+
     /**
      * Check if this IdentNode is a property name
      * @return true if this is a property name
--- a/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/ir/LiteralNode.java	Tue Oct 29 10:40:00 2013 -0300
@@ -275,6 +275,11 @@
         public boolean isTrue() {
             return JSType.toBoolean(value);
         }
+
+        @Override
+        public boolean isLocal() {
+            return true;
+        }
     }
 
     @Immutable
--- a/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/ir/TernaryNode.java	Tue Oct 29 10:40:00 2013 -0300
@@ -109,6 +109,13 @@
         }
     }
 
+    @Override
+    public boolean isLocal() {
+        return getTest().isLocal()
+                && getTrueExpression().isLocal()
+                && getFalseExpression().isLocal();
+    }
+
     /**
      * Get the test expression for this ternary expression, i.e. "x" in x ? y : z
      * @return the test expression
--- a/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/ir/UnaryNode.java	Tue Oct 29 10:40:00 2013 -0300
@@ -129,6 +129,26 @@
     }
 
     @Override
+    public boolean isLocal() {
+        switch (tokenType()) {
+            case NEW:
+                return false;
+            case ADD:
+            case SUB:
+            case NOT:
+            case BIT_NOT:
+                return rhs.isLocal() && rhs.getType().isJSPrimitive();
+            case DECPOSTFIX:
+            case DECPREFIX:
+            case INCPOSTFIX:
+            case INCPREFIX:
+                return rhs instanceof IdentNode && rhs.isLocal() && rhs.getType().isJSPrimitive();
+            default:
+                return rhs.isLocal();
+        }
+    }
+
+    @Override
     public void toString(final StringBuilder sb) {
         toString(sb, new Runnable() {
             @Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/CompiledFunctions.java	Tue Oct 29 10:40:00 2013 -0300
@@ -24,6 +24,7 @@
  */
 package jdk.nashorn.internal.runtime;
 
+import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodType;
 import java.util.Iterator;
 import java.util.TreeSet;
@@ -35,6 +36,8 @@
 @SuppressWarnings("serial")
 final class CompiledFunctions extends TreeSet<CompiledFunction> {
 
+    private CompiledFunction generic;
+
     CompiledFunction best(final MethodType type) {
         final Iterator<CompiledFunction> iter = iterator();
         while (iter.hasNext()) {
@@ -43,13 +46,10 @@
                 return next;
             }
         }
-        return mostGeneric();
+        return generic();
     }
 
     boolean needsCallee() {
-        for (final CompiledFunction inv : this) {
-            assert ScriptFunctionData.needsCallee(inv.getInvoker()) == ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
-        }
         return ScriptFunctionData.needsCallee(mostGeneric().getInvoker());
     }
 
@@ -57,6 +57,48 @@
         return last();
     }
 
+    CompiledFunction generic() {
+        CompiledFunction gen = this.generic;
+        if (gen == null) {
+            gen = this.generic = makeGeneric(mostGeneric());
+        }
+        return gen;
+    }
+
+    private static CompiledFunction makeGeneric(final CompiledFunction func) {
+        final MethodHandle invoker = composeGenericMethod(func.getInvoker());
+        final MethodHandle constructor = func.hasConstructor() ? composeGenericMethod(func.getConstructor()) : null;
+        return new CompiledFunction(invoker.type(), invoker, constructor);
+    }
+
+    /**
+     * Takes a method handle, and returns a potentially different method handle that can be used in
+     * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
+     * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
+     * {@code Object} as well, except for the following ones:
+     * <ul>
+     *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
+     *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
+     *   (callee) as an argument.</li>
+     * </ul>
+     *
+     * @param mh the original method handle
+     *
+     * @return the new handle, conforming to the rules above.
+     */
+    private static MethodHandle composeGenericMethod(final MethodHandle mh) {
+        final MethodType type = mh.type();
+        final boolean isVarArg = ScriptFunctionData.isVarArg(mh);
+        final int paramCount = isVarArg ? type.parameterCount() - 1 : type.parameterCount();
+
+        MethodType newType = MethodType.genericMethodType(paramCount, isVarArg);
+
+        if (ScriptFunctionData.needsCallee(mh)) {
+            newType = newType.changeParameterType(0, ScriptFunction.class);
+        }
+        return type.equals(newType) ? mh : mh.asType(newType);
+    }
+
     /**
      * Is the given type even more specific than this entire list? That means
      * we have an opportunity for more specific versions of the method
--- a/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java	Tue Oct 29 10:40:00 2013 -0300
@@ -40,7 +40,7 @@
      *
      * @param name          name
      * @param arity         arity
-     * @param list          precompiled code
+     * @param functions     precompiled code
      * @param isStrict      strict
      * @param isBuiltin     builtin
      * @param isConstructor constructor
@@ -73,12 +73,13 @@
     }
 
     private void addInvoker(final MethodHandle mh) {
-        boolean needsCallee = needsCallee(mh);
         if (isConstructor(mh)) {
-            //only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
-            //is too conservative a check. However, isConstructor(mh) always implies isConstructor param
+            // only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
+            // is too conservative a check. However, isConstructor(mh) always implies isConstructor param
             assert isConstructor();
-            code.add(new CompiledFunction(mh.type(), MH.insertArguments(mh, 0, false), composeConstructor(MH.insertArguments(mh, 0, true), needsCallee))); //make sure callee state can be determined when we reach constructor
+            final MethodHandle invoker = MH.insertArguments(mh, 0, false);
+            final MethodHandle constructor = composeConstructor(MH.insertArguments(mh, 0, true));
+            code.add(new CompiledFunction(mh.type(), invoker, constructor));
         } else {
             code.add(new CompiledFunction(mh.type(), mh));
         }
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Mon Oct 28 12:29:40 2013 -0700
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java	Tue Oct 29 10:40:00 2013 -0300
@@ -213,13 +213,13 @@
      */
     public final MethodHandle getGenericInvoker() {
         ensureCodeGenerated();
-        return composeGenericMethod(code.mostGeneric().getInvoker());
+        return code.generic().getInvoker();
     }
 
     final MethodHandle getGenericConstructor() {
         ensureCodeGenerated();
-        ensureConstructor(code.mostGeneric());
-        return composeGenericMethod(code.mostGeneric().getConstructor());
+        ensureConstructor(code.generic());
+        return code.generic().getConstructor();
     }
 
     private CompiledFunction getBest(final MethodType callSiteType) {
@@ -267,18 +267,17 @@
     }
 
     /**
-     * Compose a constructor given a primordial constructor handle
+     * Compose a constructor given a primordial constructor handle.
      *
-     * @param ctor         primordial constructor handle
-     * @param needsCallee  do we need to pass a callee
-     *
+     * @param ctor primordial constructor handle
      * @return the composed constructor
      */
-    protected MethodHandle composeConstructor(final MethodHandle ctor, final boolean needsCallee) {
+    protected MethodHandle composeConstructor(final MethodHandle ctor) {
         // If it was (callee, this, args...), permute it to (this, callee, args...). We're doing this because having
         // "this" in the first argument position is what allows the elegant folded composition of
         // (newFilter x constructor x allocator) further down below in the code. Also, ensure the composite constructor
         // always returns Object.
+        final boolean needsCallee = needsCallee(ctor);
         MethodHandle composedCtor = needsCallee ? swapCalleeAndThis(ctor) : ctor;
 
         composedCtor = changeReturnTypeToObject(composedCtor);
@@ -472,33 +471,6 @@
     }
 
     /**
-     * Takes a method handle, and returns a potentially different method handle that can be used in
-     * {@code ScriptFunction#invoke(Object, Object...)} or {code ScriptFunction#construct(Object, Object...)}.
-     * The returned method handle will be sure to return {@code Object}, and will have all its parameters turned into
-     * {@code Object} as well, except for the following ones:
-     * <ul>
-     *   <li>a last parameter of type {@code Object[]} which is used for vararg functions,</li>
-     *   <li>the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself
-     *   (callee) as an argument.</li>
-     * </ul>
-     *
-     * @param mh the original method handle
-     *
-     * @return the new handle, conforming to the rules above.
-     */
-    protected MethodHandle composeGenericMethod(final MethodHandle mh) {
-        final MethodType type = mh.type();
-        MethodType newType = type.generic();
-        if (isVarArg(mh)) {
-            newType = newType.changeParameterType(type.parameterCount() - 1, Object[].class);
-        }
-        if (needsCallee(mh)) {
-            newType = newType.changeParameterType(0, ScriptFunction.class);
-        }
-        return type.equals(newType) ? mh : mh.asType(newType);
-    }
-
-    /**
      * Execute this script function.
      *
      * @param self  Target object.
@@ -508,10 +480,9 @@
      * @throws Throwable if there is an exception/error with the invocation or thrown from it
      */
     Object invoke(final ScriptFunction fn, final Object self, final Object... arguments) throws Throwable {
-        final MethodHandle mh = getGenericInvoker();
-
-        final Object       selfObj    = convertThisObject(self);
-        final Object[]     args       = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
+        final MethodHandle mh  = getGenericInvoker();
+        final Object   selfObj = convertThisObject(self);
+        final Object[] args    = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
         if (isVarArg(mh)) {
             if (needsCallee(mh)) {
@@ -531,6 +502,12 @@
                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1));
             case 5:
                 return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+            case 6:
+                return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
+            case 7:
+                return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
+            case 8:
+                return mh.invokeExact(fn, selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
             default:
                 return mh.invokeWithArguments(withArguments(fn, selfObj, paramCount, args));
             }
@@ -545,15 +522,20 @@
             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1));
         case 4:
             return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+        case 5:
+            return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
+        case 6:
+            return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
+        case 7:
+            return mh.invokeExact(selfObj, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
         default:
             return mh.invokeWithArguments(withArguments(null, selfObj, paramCount, args));
         }
     }
 
     Object construct(final ScriptFunction fn, final Object... arguments) throws Throwable {
-        final MethodHandle mh = getGenericConstructor();
-
-        final Object[]     args       = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
+        final MethodHandle mh   = getGenericConstructor();
+        final Object[]     args = arguments == null ? ScriptRuntime.EMPTY_ARRAY : arguments;
 
         if (isVarArg(mh)) {
             if (needsCallee(mh)) {
@@ -573,6 +555,12 @@
                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1));
             case 4:
                 return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2));
+            case 5:
+                return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
+            case 6:
+                return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
+            case 7:
+                return mh.invokeExact(fn, getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
             default:
                 return mh.invokeWithArguments(withArguments(fn, paramCount, args));
             }
@@ -587,6 +575,12 @@
             return mh.invokeExact(getArg(args, 0), getArg(args, 1));
         case 3:
             return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2));
+        case 4:
+            return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3));
+        case 5:
+            return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4));
+        case 6:
+            return mh.invokeExact(getArg(args, 0), getArg(args, 1), getArg(args, 2), getArg(args, 3), getArg(args, 4), getArg(args, 5));
         default:
             return mh.invokeWithArguments(withArguments(null, paramCount, args));
         }
@@ -664,20 +658,21 @@
      * @return the adapted handle
      */
     private static MethodHandle changeReturnTypeToObject(final MethodHandle mh) {
-        return MH.asType(mh, mh.type().changeReturnType(Object.class));
+        final MethodType type = mh.type();
+        return (type.returnType() == Object.class) ? mh : MH.asType(mh, type.changeReturnType(Object.class));
     }
 
     private void ensureConstructor(final CompiledFunction inv) {
         if (!inv.hasConstructor()) {
-            inv.setConstructor(composeConstructor(inv.getInvoker(), needsCallee(inv.getInvoker())));
+            inv.setConstructor(composeConstructor(inv.getInvoker()));
         }
     }
 
     /**
-     * Heuristic to figure out if the method handle has a callee argument. If it's type is either
-     * {@code (boolean, ScriptFunction, ...)} or {@code (ScriptFunction, ...)}, then we'll assume it has
-     * a callee argument. We need this as the constructor above is not passed this information, and can't just blindly
-     * assume it's false (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
+     * Heuristic to figure out if the method handle has a callee argument. If it's type is
+     * {@code (ScriptFunction, ...)}, then we'll assume it has a callee argument. We need this as
+     * the constructor above is not passed this information, and can't just blindly assume it's false
+     * (notably, it's being invoked for creation of new scripts, and scripts have scopes, therefore
      * they also always receive a callee).
      *
      * @param mh the examined method handle
@@ -685,18 +680,8 @@
      * @return true if the method handle expects a callee, false otherwise
      */
     protected static boolean needsCallee(final MethodHandle mh) {
-        final MethodType type   = mh.type();
-        final int        length = type.parameterCount();
-
-        if (length == 0) {
-            return false;
-        }
-
-        if (type.parameterType(0) == ScriptFunction.class) {
-            return true;
-        }
-
-        return length > 1 && type.parameterType(0) == boolean.class && type.parameterType(1) == ScriptFunction.class;
+        final MethodType type = mh.type();
+        return (type.parameterCount() > 0 && type.parameterType(0) == ScriptFunction.class);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8027042.js	Tue Oct 29 10:40:00 2013 -0300
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8027042: Evaluation order for binary operators can be improved
+ *
+ * @test
+ * @run
+ */
+
+// var with getter side effect
+Object.defineProperty(this, "a", { get: function() {print("get a"); return 1; }});
+
+// var with both getter and conversion side effect
+Object.defineProperty(this, "b", { get: function() {print("get b"); return {valueOf: function() { print("conv b"); return 10; }}; }});
+
+(function() {
+    // var with toPrimitive conversion side effect
+    var c = {valueOf: function() { print("conv c"); return 100; }};
+
+    print(b + (c + a));
+    print(b + (c + b));
+    print(b + (a + b));
+    print(b + (b + c));
+    print(b + (b + c));
+    print(b + (c + (a - b)));
+    print(b + (c + (c - b)));
+    print(b + (c + (b - c)));
+    print(b + (b + (a ? 2 : 3)));
+    print(b + (b + (b ? 2 : 3)));
+    print(b + (b + (c ? 2 : 3)));
+    print(b + ((-c) + (-a)));
+    print(b + ((-c) + (-b)));
+    print(b + ((-c) + (-c)));
+    try { print(b + new a); } catch (e) {}
+    try { print(b + new b); } catch (e) {}
+    try { print(b + new c); } catch (e) {}
+})();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8027042.js.EXPECTED	Tue Oct 29 10:40:00 2013 -0300
@@ -0,0 +1,88 @@
+get b
+get a
+conv c
+conv b
+111
+get b
+get b
+conv c
+conv b
+conv b
+120
+get b
+get a
+get b
+conv b
+conv b
+21
+get b
+get b
+conv b
+conv c
+conv b
+120
+get b
+get b
+conv b
+conv c
+conv b
+120
+get b
+get a
+get b
+conv b
+conv c
+conv b
+101
+get b
+get b
+conv c
+conv b
+conv c
+conv b
+200
+get b
+get b
+conv b
+conv c
+conv c
+conv b
+20
+get b
+get b
+get a
+conv b
+conv b
+22
+get b
+get b
+get b
+conv b
+conv b
+22
+get b
+get b
+conv b
+conv b
+22
+get b
+conv c
+get a
+conv b
+-91
+get b
+conv c
+get b
+conv b
+conv b
+-100
+get b
+conv c
+conv c
+conv b
+-190
+get b
+get a
+get b
+get b
+get b