8069338: Implement sharedScopeCall for optimistic types
authorhannesw
Tue, 12 Dec 2017 15:38:18 +0100
changeset 48247 fa5a47cad0c9
parent 48246 aadc02050d3b
child 48248 55b9b1e184c6
8069338: Implement sharedScopeCall for optimistic types Reviewed-by: attila, sundar
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGeneratorLexicalContext.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SharedScopeCall.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/UnwarrantedOptimismException.java
test/nashorn/script/basic/JDK-8069338.js
--- 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');
+