# HG changeset patch # User jlaskey # Date 1383054000 10800 # Node ID 406f2b672937b1e9ea433535e10f6a16255b498f # Parent 767e85d2a1b34f15edf29779a494c56254a28acb# Parent 71cfb21c68dc0916052a840ec514dc0548b4bf42 Merge diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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; diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/codegen/types/Type.java --- a/src/jdk/nashorn/internal/codegen/types/Type.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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 and Object will produce Object // TODO: maybe find most specific common superclass? return Type.OBJECT; diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/ir/BinaryNode.java --- a/src/jdk/nashorn/internal/ir/BinaryNode.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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(); diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/ir/Expression.java --- a/src/jdk/nashorn/internal/ir/Expression.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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; + } } diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/ir/IdentNode.java --- a/src/jdk/nashorn/internal/ir/IdentNode.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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 diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/ir/LiteralNode.java --- a/src/jdk/nashorn/internal/ir/LiteralNode.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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 diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/ir/TernaryNode.java --- a/src/jdk/nashorn/internal/ir/TernaryNode.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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 diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/ir/UnaryNode.java --- a/src/jdk/nashorn/internal/ir/UnaryNode.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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 diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/runtime/CompiledFunctions.java --- a/src/jdk/nashorn/internal/runtime/CompiledFunctions.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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 { + private CompiledFunction generic; + CompiledFunction best(final MethodType type) { final Iterator 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: + *
    + *
  • a last parameter of type {@code Object[]} which is used for vararg functions,
  • + *
  • the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself + * (callee) as an argument.
  • + *
+ * + * @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 diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java --- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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)); } diff -r 767e85d2a1b3 -r 406f2b672937 src/jdk/nashorn/internal/runtime/ScriptFunctionData.java --- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Mon Oct 28 12:29:40 2013 -0700 +++ b/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: - *
    - *
  • a last parameter of type {@code Object[]} which is used for vararg functions,
  • - *
  • the first argument, which is forced to be {@link ScriptFunction}, in case the function receives itself - * (callee) as an argument.
  • - *
- * - * @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); } /** diff -r 767e85d2a1b3 -r 406f2b672937 test/script/basic/JDK-8027042.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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) {} +})(); diff -r 767e85d2a1b3 -r 406f2b672937 test/script/basic/JDK-8027042.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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