8062308: Incorrect constant linkage with multiple Globals in a Context

Thu, 06 Nov 2014 17:06:56 +0100

author
attila
date
Thu, 06 Nov 2014 17:06:56 +0100
changeset 1089
981feb6ad9cc
parent 1088
b49b6786afad
child 1090
99571b7922c0

8062308: Incorrect constant linkage with multiple Globals in a Context
Reviewed-by: lagergren, sundar

src/jdk/nashorn/internal/objects/Global.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/Context.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/GlobalConstants.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/ScriptObject.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/objects/Global.java	Thu Nov 06 13:15:52 2014 +0100
     1.2 +++ b/src/jdk/nashorn/internal/objects/Global.java	Thu Nov 06 17:06:56 2014 +0100
     1.3 @@ -29,6 +29,7 @@
     1.4  import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
     1.5  import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
     1.6  import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
     1.7 +
     1.8  import java.io.IOException;
     1.9  import java.io.PrintWriter;
    1.10  import java.lang.invoke.MethodHandle;
    1.11 @@ -41,7 +42,6 @@
    1.12  import java.util.Map;
    1.13  import java.util.concurrent.Callable;
    1.14  import java.util.concurrent.ConcurrentHashMap;
    1.15 -import java.util.concurrent.atomic.AtomicReference;
    1.16  import javax.script.ScriptContext;
    1.17  import javax.script.ScriptEngine;
    1.18  import jdk.internal.dynalink.linker.GuardedInvocation;
    1.19 @@ -54,7 +54,6 @@
    1.20  import jdk.nashorn.internal.objects.annotations.ScriptClass;
    1.21  import jdk.nashorn.internal.runtime.ConsString;
    1.22  import jdk.nashorn.internal.runtime.Context;
    1.23 -import jdk.nashorn.internal.runtime.GlobalConstants;
    1.24  import jdk.nashorn.internal.runtime.GlobalFunctions;
    1.25  import jdk.nashorn.internal.runtime.JSType;
    1.26  import jdk.nashorn.internal.runtime.NativeJavaPackage;
    1.27 @@ -438,9 +437,6 @@
    1.28          this.scontext = scontext;
    1.29      }
    1.30  
    1.31 -    // global constants for this global - they can be replaced with MethodHandle.constant until invalidated
    1.32 -    private static AtomicReference<GlobalConstants> gcsInstance = new AtomicReference<>();
    1.33 -
    1.34      @Override
    1.35      protected Context getContext() {
    1.36          return context;
    1.37 @@ -470,11 +466,6 @@
    1.38          super(checkAndGetMap(context));
    1.39          this.context = context;
    1.40          this.setIsScope();
    1.41 -        //we can only share one instance of Global constants between globals, or we consume way too much
    1.42 -        //memory - this is good enough for most programs
    1.43 -        while (gcsInstance.get() == null) {
    1.44 -            gcsInstance.compareAndSet(null, new GlobalConstants(context.getLogger(GlobalConstants.class)));
    1.45 -        }
    1.46      }
    1.47  
    1.48      /**
    1.49 @@ -493,15 +484,6 @@
    1.50      }
    1.51  
    1.52      /**
    1.53 -     * Return the global constants map for fields that
    1.54 -     * can be accessed as MethodHandle.constant
    1.55 -     * @return constant map
    1.56 -     */
    1.57 -    public static GlobalConstants getConstants() {
    1.58 -        return gcsInstance.get();
    1.59 -    }
    1.60 -
    1.61 -    /**
    1.62       * Check if we have a Global instance
    1.63       * @return true if one exists
    1.64       */
     2.1 --- a/src/jdk/nashorn/internal/runtime/Context.java	Thu Nov 06 13:15:52 2014 +0100
     2.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java	Thu Nov 06 17:06:56 2014 +0100
     2.3 @@ -33,6 +33,7 @@
     2.4  import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
     2.5  import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
     2.6  import static jdk.nashorn.internal.runtime.Source.sourceFor;
     2.7 +
     2.8  import java.io.File;
     2.9  import java.io.IOException;
    2.10  import java.io.PrintWriter;
    2.11 @@ -60,6 +61,7 @@
    2.12  import java.util.LinkedHashMap;
    2.13  import java.util.Map;
    2.14  import java.util.concurrent.atomic.AtomicLong;
    2.15 +import java.util.concurrent.atomic.AtomicReference;
    2.16  import java.util.function.Consumer;
    2.17  import java.util.function.Supplier;
    2.18  import java.util.logging.Level;
    2.19 @@ -262,6 +264,10 @@
    2.20      // persistent code store
    2.21      private CodeStore codeStore;
    2.22  
    2.23 +    // A factory for linking global properties as constant method handles. It is created when the first Global
    2.24 +    // is created, and invalidated forever once the second global is created.
    2.25 +    private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
    2.26 +
    2.27      /**
    2.28       * Get the current global scope
    2.29       * @return the current global scope
    2.30 @@ -293,7 +299,10 @@
    2.31          assert getGlobal() != global;
    2.32          //same code can be cached between globals, then we need to invalidate method handle constants
    2.33          if (global != null) {
    2.34 -            Global.getConstants().invalidateAll();
    2.35 +            final GlobalConstants globalConstants = getContext(global).getGlobalConstants();
    2.36 +            if (globalConstants != null) {
    2.37 +                globalConstants.invalidateAll();
    2.38 +            }
    2.39          }
    2.40          currentGlobal.set(global);
    2.41      }
    2.42 @@ -529,6 +538,15 @@
    2.43      }
    2.44  
    2.45      /**
    2.46 +     * Returns the factory for constant method handles for global properties. The returned factory can be
    2.47 +     * invalidated if this Context has more than one Global.
    2.48 +     * @return the factory for constant method handles for global properties.
    2.49 +     */
    2.50 +    GlobalConstants getGlobalConstants() {
    2.51 +        return globalConstantsRef.get();
    2.52 +    }
    2.53 +
    2.54 +    /**
    2.55       * Get the error manager for this context
    2.56       * @return error manger
    2.57       */
    2.58 @@ -1016,9 +1034,32 @@
    2.59       * @return the global script object
    2.60       */
    2.61      public Global newGlobal() {
    2.62 +        createOrInvalidateGlobalConstants();
    2.63          return new Global(this);
    2.64      }
    2.65  
    2.66 +    private void createOrInvalidateGlobalConstants() {
    2.67 +        for (;;) {
    2.68 +            final GlobalConstants currentGlobalConstants = getGlobalConstants();
    2.69 +            if (currentGlobalConstants != null) {
    2.70 +                // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use
    2.71 +                // with more than one Global, as the constant method handle linkages it creates create a coupling
    2.72 +                // between the Global and the call sites in the compiled code.
    2.73 +                currentGlobalConstants.invalidateForever();
    2.74 +                return;
    2.75 +            }
    2.76 +            final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class));
    2.77 +            if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) {
    2.78 +                // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object
    2.79 +                // for this Context.
    2.80 +                return;
    2.81 +            }
    2.82 +
    2.83 +            // If we reach here, then we started out as the first invocation, but another concurrent invocation won the
    2.84 +            // CAS race. We'll just let the loop repeat and invalidate the CAS race winner.
    2.85 +        }
    2.86 +    }
    2.87 +
    2.88      /**
    2.89       * Initialize given global scope object.
    2.90       *
    2.91 @@ -1057,12 +1098,19 @@
    2.92       * @return current global's context
    2.93       */
    2.94      static Context getContextTrusted() {
    2.95 -        return ((ScriptObject)Context.getGlobal()).getContext();
    2.96 +        return getContext(getGlobal());
    2.97      }
    2.98  
    2.99      static Context getContextTrustedOrNull() {
   2.100          final Global global = Context.getGlobal();
   2.101 -        return global == null ? null : ((ScriptObject)global).getContext();
   2.102 +        return global == null ? null : getContext(global);
   2.103 +    }
   2.104 +
   2.105 +    private static Context getContext(final Global global) {
   2.106 +        // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package.
   2.107 +        // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let
   2.108 +        // virtual invocation do its thing.
   2.109 +        return ((ScriptObject)global).getContext();
   2.110      }
   2.111  
   2.112      /**
     3.1 --- a/src/jdk/nashorn/internal/runtime/GlobalConstants.java	Thu Nov 06 13:15:52 2014 +0100
     3.2 +++ b/src/jdk/nashorn/internal/runtime/GlobalConstants.java	Thu Nov 06 17:06:56 2014 +0100
     3.3 @@ -31,12 +31,14 @@
     3.4  import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
     3.5  import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint;
     3.6  import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
     3.7 +
     3.8  import java.lang.invoke.MethodHandle;
     3.9  import java.lang.invoke.MethodHandles;
    3.10  import java.lang.invoke.SwitchPoint;
    3.11  import java.util.Arrays;
    3.12  import java.util.HashMap;
    3.13  import java.util.Map;
    3.14 +import java.util.concurrent.atomic.AtomicBoolean;
    3.15  import java.util.logging.Level;
    3.16  import jdk.internal.dynalink.CallSiteDescriptor;
    3.17  import jdk.internal.dynalink.DynamicLinker;
    3.18 @@ -50,7 +52,7 @@
    3.19  import jdk.nashorn.internal.runtime.logging.Logger;
    3.20  
    3.21  /**
    3.22 - * Each global owns one of these. This is basically table of accessors
    3.23 + * Each context owns one of these. This is basically table of accessors
    3.24   * for global properties. A global constant is evaluated to a MethodHandle.constant
    3.25   * for faster access and to avoid walking to proto chain looking for it.
    3.26   *
    3.27 @@ -67,12 +69,19 @@
    3.28   * reregister the switchpoint. Set twice or more - don't try again forever, or we'd
    3.29   * just end up relinking our way into megamorphisism.
    3.30   *
    3.31 + * Also it has to be noted that this kind of linking creates a coupling between a Global
    3.32 + * and the call sites in compiled code belonging to the Context. For this reason, the
    3.33 + * linkage becomes incorrect as soon as the Context has more than one Global. The
    3.34 + * {@link #invalidateForever()} is invoked by the Context to invalidate all linkages and
    3.35 + * turn off the functionality of this object as soon as the Context's {@link Context#newGlobal()} is invoked
    3.36 + * for second time.
    3.37 + *
    3.38   * We can extend this to ScriptObjects in general (GLOBAL_ONLY=false), which requires
    3.39   * a receiver guard on the constant getter, but it currently leaks memory and its benefits
    3.40   * have not yet been investigated property.
    3.41   *
    3.42 - * As long as all Globals share the same constant instance, we need synchronization
    3.43 - * whenever we access the instance.
    3.44 + * As long as all Globals in a Context share the same GlobalConstants instance, we need synchronization
    3.45 + * whenever we access it.
    3.46   */
    3.47  @Logger(name="const")
    3.48  public final class GlobalConstants implements Loggable {
    3.49 @@ -82,7 +91,7 @@
    3.50       * Script objects require a receiver guard, which is memory intensive, so this is currently
    3.51       * disabled. We might implement a weak reference based approach to this later.
    3.52       */
    3.53 -    private static final boolean GLOBAL_ONLY = true;
    3.54 +    public static final boolean GLOBAL_ONLY = true;
    3.55  
    3.56      private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
    3.57  
    3.58 @@ -98,6 +107,8 @@
    3.59       */
    3.60      private final Map<String, Access> map = new HashMap<>();
    3.61  
    3.62 +    private final AtomicBoolean invalidatedForever = new AtomicBoolean(false);
    3.63 +
    3.64      /**
    3.65       * Constructor - used only by global
    3.66       * @param log logger, or null if none
    3.67 @@ -216,10 +227,34 @@
    3.68       * the same class for a new global, but the builtins and global scoped variables
    3.69       * will have changed.
    3.70       */
    3.71 -    public synchronized void invalidateAll() {
    3.72 -        log.info("New global created - invalidating all constant callsites without increasing invocation count.");
    3.73 -        for (final Access acc : map.values()) {
    3.74 -            acc.invalidateUncounted();
    3.75 +    public void invalidateAll() {
    3.76 +        if (!invalidatedForever.get()) {
    3.77 +            log.info("New global created - invalidating all constant callsites without increasing invocation count.");
    3.78 +            synchronized (this) {
    3.79 +                for (final Access acc : map.values()) {
    3.80 +                    acc.invalidateUncounted();
    3.81 +                }
    3.82 +            }
    3.83 +        }
    3.84 +    }
    3.85 +
    3.86 +    /**
    3.87 +     * To avoid an expensive global guard "is this the same global", similar to the
    3.88 +     * receiver guard on the ScriptObject level, we invalidate all getters when the
    3.89 +     * second Global is created by the Context owning this instance. After this
    3.90 +     * method is invoked, this GlobalConstants instance will both invalidate all the
    3.91 +     * switch points it produced, and it will stop handing out new method handles
    3.92 +     * altogether.
    3.93 +     */
    3.94 +    public void invalidateForever() {
    3.95 +        if (invalidatedForever.compareAndSet(false, true)) {
    3.96 +            log.info("New global created - invalidating all constant callsites.");
    3.97 +            synchronized (this) {
    3.98 +                for (final Access acc : map.values()) {
    3.99 +                    acc.invalidateForever();
   3.100 +                }
   3.101 +                map.clear();
   3.102 +            }
   3.103          }
   3.104      }
   3.105  
   3.106 @@ -251,7 +286,7 @@
   3.107          return obj;
   3.108      }
   3.109  
   3.110 -    private synchronized Access getOrCreateSwitchPoint(final String name) {
   3.111 +    private Access getOrCreateSwitchPoint(final String name) {
   3.112          Access acc = map.get(name);
   3.113          if (acc != null) {
   3.114              return acc;
   3.115 @@ -267,9 +302,13 @@
   3.116       * @param name name of property
   3.117       */
   3.118      void delete(final String name) {
   3.119 -        final Access acc = map.get(name);
   3.120 -        if (acc != null) {
   3.121 -            acc.invalidateForever();
   3.122 +        if (!invalidatedForever.get()) {
   3.123 +            synchronized (this) {
   3.124 +                final Access acc = map.get(name);
   3.125 +                if (acc != null) {
   3.126 +                    acc.invalidateForever();
   3.127 +                }
   3.128 +            }
   3.129          }
   3.130      }
   3.131  
   3.132 @@ -313,45 +352,45 @@
   3.133       *
   3.134       * @return null if failed to set up constant linkage
   3.135       */
   3.136 -    synchronized GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
   3.137 -        if (GLOBAL_ONLY && !isGlobalSetter(receiver, find)) {
   3.138 +    GuardedInvocation findSetMethod(final FindProperty find, final ScriptObject receiver, final GuardedInvocation inv, final CallSiteDescriptor desc, final LinkRequest request) {
   3.139 +        if (invalidatedForever.get() || (GLOBAL_ONLY && !isGlobalSetter(receiver, find))) {
   3.140              return null;
   3.141          }
   3.142  
   3.143          final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
   3.144  
   3.145 -        final Access acc  = getOrCreateSwitchPoint(name);
   3.146 +        synchronized (this) {
   3.147 +            final Access acc  = getOrCreateSwitchPoint(name);
   3.148  
   3.149 -        if (log.isEnabled()) {
   3.150 -            log.fine("Trying to link constant SETTER ", acc);
   3.151 +            if (log.isEnabled()) {
   3.152 +                log.fine("Trying to link constant SETTER ", acc);
   3.153 +            }
   3.154 +
   3.155 +            if (!acc.mayRetry() || invalidatedForever.get()) {
   3.156 +                if (log.isEnabled()) {
   3.157 +                    log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
   3.158 +                }
   3.159 +                return null;
   3.160 +            }
   3.161 +
   3.162 +            if (acc.hasBeenInvalidated()) {
   3.163 +                log.info("New chance for " + acc);
   3.164 +                acc.newSwitchPoint();
   3.165 +            }
   3.166 +
   3.167 +            assert !acc.hasBeenInvalidated();
   3.168 +
   3.169 +            // if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
   3.170 +            final MethodHandle target           = inv.getInvocation();
   3.171 +            final Class<?>     receiverType     = target.type().parameterType(0);
   3.172 +            final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP,  this);
   3.173 +            final MethodHandle invalidator      = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
   3.174 +            final MethodHandle mh               = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
   3.175 +
   3.176 +            assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
   3.177 +            log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
   3.178 +            return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
   3.179          }
   3.180 -
   3.181 -        if (!acc.mayRetry()) {
   3.182 -            if (log.isEnabled()) {
   3.183 -                log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
   3.184 -            }
   3.185 -            return null;
   3.186 -        }
   3.187 -
   3.188 -        assert acc.mayRetry();
   3.189 -
   3.190 -        if (acc.hasBeenInvalidated()) {
   3.191 -            log.info("New chance for " + acc);
   3.192 -            acc.newSwitchPoint();
   3.193 -        }
   3.194 -
   3.195 -        assert !acc.hasBeenInvalidated();
   3.196 -
   3.197 -        // if we haven't given up on this symbol, add a switchpoint invalidation filter to the receiver parameter
   3.198 -        final MethodHandle target           = inv.getInvocation();
   3.199 -        final Class<?>     receiverType     = target.type().parameterType(0);
   3.200 -        final MethodHandle boundInvalidator = MH.bindTo(INVALIDATE_SP,  this);
   3.201 -        final MethodHandle invalidator      = MH.asType(boundInvalidator, boundInvalidator.type().changeParameterType(0, receiverType).changeReturnType(receiverType));
   3.202 -        final MethodHandle mh               = MH.filterArguments(inv.getInvocation(), 0, MH.insertArguments(invalidator, 1, acc));
   3.203 -
   3.204 -        assert inv.getSwitchPoints() == null : Arrays.asList(inv.getSwitchPoints());
   3.205 -        log.info("Linked setter " + quote(name) + " " + acc.getSwitchPoint());
   3.206 -        return new GuardedInvocation(mh, inv.getGuard(), acc.getSwitchPoint(), inv.getException());
   3.207      }
   3.208  
   3.209      /**
   3.210 @@ -380,11 +419,11 @@
   3.211       *
   3.212       * @return resulting getter, or null if failed to create constant
   3.213       */
   3.214 -    synchronized GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
   3.215 +    GuardedInvocation findGetMethod(final FindProperty find, final ScriptObject receiver, final CallSiteDescriptor desc) {
   3.216          // Only use constant getter for fast scope access, because the receiver may change between invocations
   3.217          // for slow-scope and non-scope callsites.
   3.218          // Also return null for user accessor properties as they may have side effects.
   3.219 -        if (!NashornCallSiteDescriptor.isFastScope(desc)
   3.220 +        if (invalidatedForever.get() || !NashornCallSiteDescriptor.isFastScope(desc)
   3.221                  || (GLOBAL_ONLY && !find.getOwner().isGlobal())
   3.222                  || find.getProperty() instanceof UserAccessorProperty) {
   3.223              return null;
   3.224 @@ -395,51 +434,53 @@
   3.225          final Class<?> retType      = desc.getMethodType().returnType();
   3.226          final String   name         = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
   3.227  
   3.228 -        final Access acc = getOrCreateSwitchPoint(name);
   3.229 +        synchronized (this) {
   3.230 +            final Access acc = getOrCreateSwitchPoint(name);
   3.231  
   3.232 -        log.fine("Starting to look up object value " + name);
   3.233 -        final Object c = find.getObjectValue();
   3.234 +            log.fine("Starting to look up object value " + name);
   3.235 +            final Object c = find.getObjectValue();
   3.236  
   3.237 -        if (log.isEnabled()) {
   3.238 -            log.fine("Trying to link constant GETTER " + acc + " value = " + c);
   3.239 +            if (log.isEnabled()) {
   3.240 +                log.fine("Trying to link constant GETTER " + acc + " value = " + c);
   3.241 +            }
   3.242 +
   3.243 +            if (acc.hasBeenInvalidated() || acc.guardFailed() || invalidatedForever.get()) {
   3.244 +                if (log.isEnabled()) {
   3.245 +                    log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
   3.246 +                }
   3.247 +                return null;
   3.248 +            }
   3.249 +
   3.250 +            final MethodHandle cmh = constantGetter(c);
   3.251 +
   3.252 +            MethodHandle mh;
   3.253 +            MethodHandle guard;
   3.254 +
   3.255 +            if (isOptimistic) {
   3.256 +                if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
   3.257 +                    //widen return type - this is pessimistic, so it will always work
   3.258 +                    mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
   3.259 +                } else {
   3.260 +                    //immediately invalidate - we asked for a too wide constant as a narrower one
   3.261 +                    mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
   3.262 +                }
   3.263 +            } else {
   3.264 +                //pessimistic return type filter
   3.265 +                mh = Lookup.filterReturnType(cmh, retType);
   3.266 +            }
   3.267 +
   3.268 +            if (find.getOwner().isGlobal()) {
   3.269 +                guard = null;
   3.270 +            } else {
   3.271 +                guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
   3.272 +            }
   3.273 +
   3.274 +            if (log.isEnabled()) {
   3.275 +                log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
   3.276 +                mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
   3.277 +            }
   3.278 +
   3.279 +            return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
   3.280          }
   3.281 -
   3.282 -        if (acc.hasBeenInvalidated() || acc.guardFailed()) {
   3.283 -            if (log.isEnabled()) {
   3.284 -                log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation());
   3.285 -            }
   3.286 -            return null;
   3.287 -        }
   3.288 -
   3.289 -        final MethodHandle cmh = constantGetter(c);
   3.290 -
   3.291 -        MethodHandle mh;
   3.292 -        MethodHandle guard;
   3.293 -
   3.294 -        if (isOptimistic) {
   3.295 -            if (JSType.getAccessorTypeIndex(cmh.type().returnType()) <= JSType.getAccessorTypeIndex(retType)) {
   3.296 -                //widen return type - this is pessimistic, so it will always work
   3.297 -                mh = MH.asType(cmh, cmh.type().changeReturnType(retType));
   3.298 -            } else {
   3.299 -                //immediately invalidate - we asked for a too wide constant as a narrower one
   3.300 -                mh = MH.dropArguments(MH.insertArguments(JSType.THROW_UNWARRANTED.methodHandle(), 0, c, programPoint), 0, Object.class);
   3.301 -            }
   3.302 -        } else {
   3.303 -            //pessimistic return type filter
   3.304 -            mh = Lookup.filterReturnType(cmh, retType);
   3.305 -        }
   3.306 -
   3.307 -        if (find.getOwner().isGlobal()) {
   3.308 -            guard = null;
   3.309 -        } else {
   3.310 -            guard = MH.insertArguments(RECEIVER_GUARD, 0, acc, receiver);
   3.311 -        }
   3.312 -
   3.313 -        if (log.isEnabled()) {
   3.314 -            log.info("Linked getter " + quote(name) + " as MethodHandle.constant() -> " + c + " " + acc.getSwitchPoint());
   3.315 -            mh = MethodHandleFactory.addDebugPrintout(log, Level.FINE, mh, "get const " + acc);
   3.316 -        }
   3.317 -
   3.318 -        return new GuardedInvocation(mh, guard, acc.getSwitchPoint(), null);
   3.319      }
   3.320  }
     4.1 --- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Thu Nov 06 13:15:52 2014 +0100
     4.2 +++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Thu Nov 06 17:06:56 2014 +0100
     4.3 @@ -47,6 +47,7 @@
     4.4  import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
     4.5  import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
     4.6  import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
     4.7 +
     4.8  import java.lang.invoke.MethodHandle;
     4.9  import java.lang.invoke.MethodHandles;
    4.10  import java.lang.invoke.MethodType;
    4.11 @@ -922,7 +923,10 @@
    4.12                  if (property instanceof UserAccessorProperty) {
    4.13                      ((UserAccessorProperty)property).setAccessors(this, getMap(), null);
    4.14                  }
    4.15 -                Global.getConstants().delete(property.getKey());
    4.16 +                final GlobalConstants globalConstants = getGlobalConstants();
    4.17 +                if (globalConstants != null) {
    4.18 +                    globalConstants.delete(property.getKey());
    4.19 +                }
    4.20                  return true;
    4.21              }
    4.22          }
    4.23 @@ -1983,9 +1987,12 @@
    4.24              }
    4.25          }
    4.26  
    4.27 -        final GuardedInvocation cinv = Global.getConstants().findGetMethod(find, this, desc);
    4.28 -        if (cinv != null) {
    4.29 -            return cinv;
    4.30 +        final GlobalConstants globalConstants = getGlobalConstants();
    4.31 +        if (globalConstants != null) {
    4.32 +            final GuardedInvocation cinv = globalConstants.findGetMethod(find, this, desc);
    4.33 +            if (cinv != null) {
    4.34 +                return cinv;
    4.35 +            }
    4.36          }
    4.37  
    4.38          final Class<?> returnType = desc.getMethodType().returnType();
    4.39 @@ -2183,14 +2190,22 @@
    4.40  
    4.41          final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
    4.42  
    4.43 -        final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
    4.44 -        if (cinv != null) {
    4.45 -            return cinv;
    4.46 +        final GlobalConstants globalConstants = getGlobalConstants();
    4.47 +        if (globalConstants != null) {
    4.48 +            final GuardedInvocation cinv = globalConstants.findSetMethod(find, this, inv, desc, request);
    4.49 +            if (cinv != null) {
    4.50 +                return cinv;
    4.51 +            }
    4.52          }
    4.53  
    4.54          return inv;
    4.55      }
    4.56  
    4.57 +    private GlobalConstants getGlobalConstants() {
    4.58 +        // Avoid hitting getContext() which might be costly for a non-Global unless needed.
    4.59 +        return GlobalConstants.GLOBAL_ONLY && !isGlobal() ? null : getContext().getGlobalConstants();
    4.60 +    }
    4.61 +
    4.62      private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
    4.63          final String  name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
    4.64          if (NashornCallSiteDescriptor.isStrict(desc)) {

mercurial