# HG changeset patch # User hannesw # Date 1513089498 -3600 # Node ID fa5a47cad0c9faf19b8eff332e0413acdcee3fbb # Parent aadc02050d3b6bca9335c6767dd3e776e77e8625 8069338: Implement sharedScopeCall for optimistic types Reviewed-by: attila, sundar diff -r aadc02050d3b -r fa5a47cad0c9 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Dec 12 18:40:31 2017 +0530 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Dec 12 15:38:18 2017 +0100 @@ -346,28 +346,30 @@ assert identNode.getSymbol().isScope() : identNode + " is not in scope!"; final int flags = getScopeCallSiteFlags(symbol); - if (isFastScope(symbol)) { - // Only generate shared scope getter for fast-scope symbols so we know we can dial in correct scope. - if (symbol.getUseCount() > SharedScopeCall.FAST_SCOPE_GET_THRESHOLD && !identNode.isOptimistic()) { - // As shared scope vars are only used with non-optimistic identifiers, we switch from using TypeBounds to - // just a single definitive type, resultBounds.widest. - new OptimisticOperation(identNode, TypeBounds.OBJECT) { - @Override - void loadStack() { - method.loadCompilerConstant(SCOPE); - } - - @Override - void consumeStack() { - loadSharedScopeVar(resultBounds.widest, symbol, flags); - } - }.emit(); - } else { - new LoadFastScopeVar(identNode, resultBounds, flags).emit(); - } + if (!isFastScope(symbol)) { + // slow scope load, prototype chain must be inspected at runtime + new LoadScopeVar(identNode, resultBounds, flags).emit(); + } else if (identNode.isCompileTimePropertyName() || symbol.getUseCount() < SharedScopeCall.SHARED_GET_THRESHOLD) { + // fast scope load with known prototype depth + new LoadFastScopeVar(identNode, resultBounds, flags).emit(); } else { - //slow scope load, we have no proto depth - new LoadScopeVar(identNode, resultBounds, flags).emit(); + // Only generate shared scope getter for often used fast-scope symbols. + new OptimisticOperation(identNode, resultBounds) { + @Override + void loadStack() { + method.loadCompilerConstant(SCOPE); + final int depth = getScopeProtoDepth(lc.getCurrentBlock(), symbol); + assert depth >= 0; + method.load(depth); + method.load(getProgramPoint()); + } + + @Override + void consumeStack() { + final Type resultType = isOptimistic ? getOptimisticCoercedType() : resultBounds.widest; + lc.getScopeGet(unit, symbol, resultType, flags, isOptimistic).generateInvoke(method); + } + }.emit(); } return method; @@ -467,12 +469,6 @@ throw new AssertionError(); } - private MethodEmitter loadSharedScopeVar(final Type valueType, final Symbol symbol, final int flags) { - assert isFastScope(symbol); - method.load(getScopeProtoDepth(lc.getCurrentBlock(), symbol)); - return lc.getScopeGet(unit, symbol, valueType, flags).generateInvoke(method); - } - private class LoadScopeVar extends OptimisticOperation { final IdentNode identNode; private final int flags; @@ -551,18 +547,23 @@ if (swap) { method.swap(); } - if (depth > 1) { - method.load(depth); - method.invoke(ScriptObject.GET_PROTO_DEPTH); - } else { - method.invoke(ScriptObject.GET_PROTO); - } + invokeGetProto(depth); if (swap) { method.swap(); } } } + private void invokeGetProto(final int depth) { + assert depth > 0; + if (depth > 1) { + method.load(depth); + method.invoke(ScriptObject.GET_PROTO_DEPTH); + } else { + method.invoke(ScriptObject.GET_PROTO); + } + } + /** * Generate code that loads this node to the stack, not constraining its type * @@ -1386,12 +1387,7 @@ return; } method.loadCompilerConstant(SCOPE); - if (count > 1) { - method.load(count); - method.invoke(ScriptObject.GET_PROTO_DEPTH); - } else { - method.invoke(ScriptObject.GET_PROTO); - } + invokeGetProto(count); method.storeCompilerConstant(SCOPE); } @@ -1444,20 +1440,22 @@ final CodeGeneratorLexicalContext codegenLexicalContext = lc; function.accept(new SimpleNodeVisitor() { + private MethodEmitter sharedScopeCall(final IdentNode identNode, final int flags) { final Symbol symbol = identNode.getSymbol(); - final boolean isFastScope = isFastScope(symbol); + assert isFastScope(symbol); + new OptimisticOperation(callNode, resultBounds) { @Override void loadStack() { method.loadCompilerConstant(SCOPE); - if (isFastScope) { - method.load(getScopeProtoDepth(currentBlock, symbol)); - } else { - method.load(-1); // Bypass fast-scope code in shared callsite - } + final int depth = getScopeProtoDepth(currentBlock, symbol); + assert depth >= 0; + method.load(depth); + method.load(getProgramPoint()); loadArgs(args); } + @Override void consumeStack() { final Type[] paramTypes = method.getTypesFromStack(args.size()); @@ -1466,13 +1464,14 @@ for(int i = 0; i < paramTypes.length; ++i) { paramTypes[i] = Type.generic(paramTypes[i]); } - // As shared scope calls are only used in non-optimistic compilation, we switch from using - // TypeBounds to just a single definitive type, resultBounds.widest. + + final Type resultType = isOptimistic ? getOptimisticCoercedType() : resultBounds.widest; final SharedScopeCall scopeCall = codegenLexicalContext.getScopeCall(unit, symbol, - identNode.getType(), resultBounds.widest, paramTypes, flags); + identNode.getType(), resultType, paramTypes, flags, isOptimistic); scopeCall.generateInvoke(method); } }.emit(); + return method; } @@ -1573,15 +1572,10 @@ final int flags = getScopeCallSiteFlags(symbol); final int useCount = symbol.getUseCount(); - // Threshold for generating shared scope callsite is lower for fast scope symbols because we know - // we can dial in the correct scope. However, we also need to enable it for non-fast scopes to - // support huge scripts like mandreel.js. + // We only use shared scope calls for fast scopes if (callNode.isEval()) { evalCall(node, flags); - } else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD - || !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD - || CodeGenerator.this.lc.inDynamicScope() - || callNode.isOptimistic()) { + } else if (!isFastScope(symbol) || symbol.getUseCount() < SharedScopeCall.SHARED_CALL_THRESHOLD) { scopeCall(node, flags); } else { sharedScopeCall(node, flags); @@ -4650,7 +4644,7 @@ } private abstract class OptimisticOperation { - private final boolean isOptimistic; + final boolean isOptimistic; // expression and optimistic are the same reference private final Expression expression; private final Optimistic optimistic; @@ -4966,7 +4960,7 @@ * affect it. * @return */ - private Type getOptimisticCoercedType() { + Type getOptimisticCoercedType() { final Type optimisticType = expression.getType(); assert resultBounds.widest.widerThan(optimisticType); final Type narrowest = resultBounds.narrowest; diff -r aadc02050d3b -r fa5a47cad0c9 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Tue Dec 12 18:40:31 2017 +0530 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java Tue Dec 12 15:38:18 2017 +0100 @@ -184,10 +184,14 @@ * @param returnType the return type * @param paramTypes the parameter types * @param flags the callsite flags + * @param isOptimistic is this an optimistic call * @return an object representing a shared scope call */ - SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) { - final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags); + SharedScopeCall getScopeCall(final CompileUnit unit, final Symbol symbol, final Type valueType, + final Type returnType, final Type[] paramTypes, final int flags, + final boolean isOptimistic) { + final SharedScopeCall scopeCall = new SharedScopeCall(symbol, valueType, returnType, paramTypes, flags, + isOptimistic); if (scopeCalls.containsKey(scopeCall)) { return scopeCalls.get(scopeCall); } @@ -203,10 +207,12 @@ * @param symbol the symbol * @param valueType the type of the variable * @param flags the callsite flags - * @return an object representing a shared scope call + * @param isOptimistic is this an optimistic get + * @return an object representing a shared scope get */ - SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags) { - return getScopeCall(unit, symbol, valueType, valueType, null, flags); + SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags, + final boolean isOptimistic) { + return getScopeCall(unit, symbol, valueType, valueType, null, flags, isOptimistic); } void onEnterBlock(final Block block) { diff -r aadc02050d3b -r fa5a47cad0c9 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Tue Dec 12 18:40:31 2017 +0530 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java Tue Dec 12 15:38:18 2017 +0100 @@ -702,19 +702,21 @@ } pushType(Type.typeFor(Throwable.class)); } + /** * Start a try/catch block. * - * @param entry start label for try - * @param exit end label for try - * @param recovery start label for catch - * @param typeDescriptor type descriptor for exception + * @param entry start label for try + * @param exit end label for try + * @param recovery start label for catch + * @param clazz exception class or null for any Throwable * @param isOptimismHandler true if this is a hander for {@code UnwarrantedOptimismException}. Normally joining on a * catch handler kills temporary variables, but optimism handlers are an exception, as they need to capture * temporaries as well, so they must remain live. */ - private void _try(final Label entry, final Label exit, final Label recovery, final String typeDescriptor, final boolean isOptimismHandler) { + void _try(final Label entry, final Label exit, final Label recovery, final Class clazz, final boolean isOptimismHandler) { recovery.joinFromTry(entry.getStack(), isOptimismHandler); + final String typeDescriptor = clazz == null ? null : CompilerConstants.className(clazz); method.visitTryCatchBlock(entry.getLabel(), exit.getLabel(), recovery.getLabel(), typeDescriptor); } @@ -727,7 +729,7 @@ * @param clazz exception class */ void _try(final Label entry, final Label exit, final Label recovery, final Class clazz) { - _try(entry, exit, recovery, CompilerConstants.className(clazz), clazz == UnwarrantedOptimismException.class); + _try(entry, exit, recovery, clazz, clazz == UnwarrantedOptimismException.class); } /** diff -r aadc02050d3b -r fa5a47cad0c9 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SharedScopeCall.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SharedScopeCall.java Tue Dec 12 18:40:31 2017 +0530 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SharedScopeCall.java Tue Dec 12 15:38:18 2017 +0100 @@ -25,6 +25,7 @@ package jdk.nashorn.internal.codegen; +import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC; import java.util.Arrays; @@ -32,39 +33,52 @@ import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; +import jdk.nashorn.internal.runtime.options.Options; /** - * A scope call or get operation that can be shared by several callsites. This generates a static + * A scope call or get operation that can be shared by several call sites. This generates a static * method that wraps the invokedynamic instructions to get or call scope variables. - * The rationale for this is that initial linking of invokedynamic callsites is expensive, - * so by sharing them we can reduce startup overhead and allow very large scripts to run that otherwise wouldn't. + * The reason for this is to reduce memory footprint and initial linking overhead of huge scripts. * - *

Static methods generated by this class expect two parameters in addition to the parameters of the - * function call: The current scope object and the depth of the target scope relative to the scope argument - * for when this is known at compile-time (fast-scope access).

+ *

Static methods generated by this class expect three parameters in addition to the parameters of the + * function call: The current scope object, the depth of the target scope relative to the scope argument, + * and the program point in case the target operation is optimistic.

* - *

The second argument may be -1 for non-fast-scope symbols, in which case the scope chain is checked - * for each call. This may cause callsite invalidation when the shared method is used from different - * scopes, but such sharing of non-fast scope calls may still be necessary for very large scripts.

+ *

Optimistic operations are called with program point 0. If an UnwarrentedOptimismException + * is thrown, it is caught by the shared call method and rethrown with the program point of the invoking call site.

* - *

Scope calls must not be shared between normal callsites and callsites contained in a with - * statement as this condition is not handled by current guards and will cause a runtime error.

+ *

Shared scope calls are not used if the scope contains a with statement or a call to + * eval.

*/ class SharedScopeCall { - /** Threshold for using shared scope calls with fast scope access. */ - public static final int FAST_SCOPE_CALL_THRESHOLD = 4; - /** Threshold for using shared scope calls with slow scope access. */ - public static final int SLOW_SCOPE_CALL_THRESHOLD = 500; - /** Threshold for using shared scope gets with fast scope access. */ - public static final int FAST_SCOPE_GET_THRESHOLD = 200; + /** + * Threshold for using shared scope function calls. + */ + public static final int SHARED_CALL_THRESHOLD = + Options.getIntProperty("nashorn.shared.scope.call.threshold", 5); + /** + * Threshold for using shared scope variable getter. This is higher than for calls as lower values + * degrade performance on many scripts. + */ + public static final int SHARED_GET_THRESHOLD = + Options.getIntProperty("nashorn.shared.scope.get.threshold", 100); - final Type valueType; - final Symbol symbol; - final Type returnType; - final Type[] paramTypes; - final int flags; - final boolean isCall; + private static final CompilerConstants.Call REPLACE_PROGRAM_POINT = virtualCallNoLookup( + UnwarrantedOptimismException.class, "replaceProgramPoint", + UnwarrantedOptimismException.class, int.class); + + /** Number of fixed parameters */ + private static final int FIXED_PARAM_COUNT = 3; + + private final Type valueType; + private final Symbol symbol; + private final Type returnType; + private final Type[] paramTypes; + private final int flags; + private final boolean isCall; + private final boolean isOptimistic; private CompileUnit compileUnit; private String methodName; private String staticSignature; @@ -77,21 +91,22 @@ * @param returnType the return type * @param paramTypes the function parameter types * @param flags the callsite flags + * @param isOptimistic whether target call is optimistic and we need to handle UnwarrentedOptimismException */ - SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) { + SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, + final int flags, final boolean isOptimistic) { this.symbol = symbol; this.valueType = valueType; this.returnType = returnType; this.paramTypes = paramTypes; - assert (flags & CALLSITE_OPTIMISTIC) == 0; this.flags = flags; - // If paramTypes is not null this is a call, otherwise it's just a get. - this.isCall = paramTypes != null; + this.isCall = paramTypes != null; // If paramTypes is not null this is a call, otherwise it's just a get. + this.isOptimistic = isOptimistic; } @Override public int hashCode() { - return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags; + return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags ^ Boolean.hashCode(isOptimistic); } @Override @@ -101,7 +116,8 @@ return symbol.equals(c.symbol) && flags == c.flags && returnType.equals(c.returnType) - && Arrays.equals(paramTypes, c.paramTypes); + && Arrays.equals(paramTypes, c.paramTypes) + && isOptimistic == c.isOptimistic; } return false; } @@ -119,10 +135,9 @@ /** * Generate the invoke instruction for this shared scope call. * @param method the method emitter - * @return the method emitter */ - public MethodEmitter generateInvoke(final MethodEmitter method) { - return method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature()); + public void generateInvoke(final MethodEmitter method) { + method.invokestatic(compileUnit.getUnitClassName(), methodName, getStaticSignature()); } /** @@ -140,51 +155,79 @@ final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature()); method.begin(); - // Load correct scope by calling getProto() on the scope argument as often as specified - // by the second argument. - final Label parentLoopStart = new Label("parent_loop_start"); - final Label parentLoopDone = new Label("parent_loop_done"); + // Load correct scope by calling getProto(int) on the scope argument with the supplied depth argument method.load(Type.OBJECT, 0); - method.label(parentLoopStart); method.load(Type.INT, 1); - method.iinc(1, -1); - method.ifle(parentLoopDone); - method.invoke(ScriptObject.GET_PROTO); - method._goto(parentLoopStart); - method.label(parentLoopDone); + method.invoke(ScriptObject.GET_PROTO_DEPTH); + + assert !isCall || valueType.isObject(); // Callables are always loaded as object + + // Labels for catch of UnsupportedOptimismException + final Label beginTry; + final Label endTry; + final Label catchLabel; - assert !isCall || valueType.isObject(); // Callables are always objects - // If flags are optimistic, but we're doing a call, remove optimistic flags from the getter, as they obviously - // only apply to the call. - method.dynamicGet(valueType, symbol.getName(), isCall ? CodeGenerator.nonOptimisticFlags(flags) : flags, isCall, false); + if(isOptimistic) { + beginTry = new Label("begin_try"); + endTry = new Label("end_try"); + catchLabel = new Label("catch_label"); + method.label(beginTry); + method._try(beginTry, endTry, catchLabel, UnwarrantedOptimismException.class, false); + } else { + beginTry = endTry = catchLabel = null; + } + + // If this is an optimistic get we set the optimistic flag but don't set the program point, + // which implies a program point of 0. If optimism fails we'll replace it with the actual + // program point which caller supplied as third argument. + final int getFlags = isOptimistic && !isCall ? flags | CALLSITE_OPTIMISTIC : flags; + method.dynamicGet(valueType, symbol.getName(), getFlags, isCall, false); // If this is a get we're done, otherwise call the value as function. if (isCall) { method.convert(Type.OBJECT); // ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly. method.loadUndefined(Type.OBJECT); - int slot = 2; + int slot = FIXED_PARAM_COUNT; for (final Type type : paramTypes) { method.load(type, slot); slot += type.getSlots(); } - // Shared scope calls disabled in optimistic world. TODO is this right? - method.dynamicCall(returnType, 2 + paramTypes.length, flags, symbol.getName()); + + // Same as above, set optimistic flag but leave program point as 0. + final int callFlags = isOptimistic ? flags | CALLSITE_OPTIMISTIC : flags; + + method.dynamicCall(returnType, 2 + paramTypes.length, callFlags, symbol.getName()); + + } + + if (isOptimistic) { + method.label(endTry); } method._return(returnType); + + if (isOptimistic) { + // We caught a UnwarrantedOptimismException, replace 0 program point with actual program point + method._catch(catchLabel); + method.load(Type.INT, 2); + method.invoke(REPLACE_PROGRAM_POINT); + method.athrow(); + } + method.end(); } private String getStaticSignature() { if (staticSignature == null) { if (paramTypes == null) { - staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT); + staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT, Type.INT); } else { - final Type[] params = new Type[paramTypes.length + 2]; + final Type[] params = new Type[paramTypes.length + FIXED_PARAM_COUNT]; params[0] = Type.typeFor(ScriptObject.class); params[1] = Type.INT; - System.arraycopy(paramTypes, 0, params, 2, paramTypes.length); + params[2] = Type.INT; + System.arraycopy(paramTypes, 0, params, FIXED_PARAM_COUNT, paramTypes.length); staticSignature = Type.getMethodDescriptor(returnType, params); } } diff -r aadc02050d3b -r fa5a47cad0c9 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Tue Dec 12 18:40:31 2017 +0530 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Tue Dec 12 15:38:18 2017 +0100 @@ -1229,9 +1229,8 @@ * @return proto at given depth */ public final ScriptObject getProto(final int n) { - assert n > 0; - ScriptObject p = getProto(); - for (int i = n; --i > 0;) { + ScriptObject p = this; + for (int i = n; i > 0; i--) { p = p.getProto(); } return p; diff -r aadc02050d3b -r fa5a47cad0c9 src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java Tue Dec 12 18:40:31 2017 +0530 +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java Tue Dec 12 15:38:18 2017 +0100 @@ -153,12 +153,15 @@ } /** - * Check if we ended up with a primitive return value (even though it may be - * too wide for what we tried to do, e.g. double instead of int) - * @return true if return value is primitive + * Return a new {@code UnwarrantedOptimismException} with the same return value and the + * new program point. + * + * @param newProgramPoint new new program point + * @return the new exception instance */ - public boolean hasPrimitiveReturnValue() { - return returnValue instanceof Number || returnValue instanceof Boolean; + public UnwarrantedOptimismException replaceProgramPoint(final int newProgramPoint) { + assert isValid(newProgramPoint); + return new UnwarrantedOptimismException(returnValue, newProgramPoint, returnType); } @Override diff -r aadc02050d3b -r fa5a47cad0c9 test/nashorn/script/basic/JDK-8069338.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/nashorn/script/basic/JDK-8069338.js Tue Dec 12 15:38:18 2017 +0100 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017, 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-8069338: Implement sharedScopeCall for optimistic types + * + * @test + * @run + */ + +var c = 0; +var n = 1; + +function f() { + return c++ > 10 ? 'f' : 1; +} + +function h() { + var x = 0; + x += n; + x += n; + x += n; + x += n; + x += n; + x += n; + x += n; + x += n; + x += n; + x += n; + x += n; + n = 'b'; + x += n; + x += n; + x += n; + x += n; + x += n; + x += n; + return x; +} + +function g() { + var x = 0; + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + x += f(); + return x; +} + +Assert.assertEquals(h(), '11bbbbbb'); +Assert.assertEquals(g(), '11ffffff'); +