8069338: Implement sharedScopeCall for optimistic types
Reviewed-by: attila, sundar
--- 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;
--- 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) {
--- 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);
}
/**
--- 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.
*
- * <p>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).</p>
+ * <p>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.</p>
*
- * <p>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.</p>
+ * <p>Optimistic operations are called with program point <code>0</code>. If an <code>UnwarrentedOptimismException</code>
+ * is thrown, it is caught by the shared call method and rethrown with the program point of the invoking call site.</p>
*
- * <p>Scope calls must not be shared between normal callsites and callsites contained in a <tt>with</tt>
- * statement as this condition is not handled by current guards and will cause a runtime error.</p>
+ * <p>Shared scope calls are not used if the scope contains a <code>with</code> statement or a call to
+ * <code>eval</code>.</p>
*/
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);
}
}
--- 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;
--- 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
--- /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');
+