1 /*
   2  * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.runtime;
  27 
  28 import java.lang.invoke.SwitchPoint;
  29 import java.util.Collections;
  30 import java.util.HashMap;
  31 import java.util.Map;
  32 import java.util.Set;
  33 import java.util.WeakHashMap;
  34 import java.util.concurrent.atomic.LongAdder;
  35 
  36 /**
  37  * Helper class for tracking and invalidation of switchpoints for inherited properties.
  38  */
  39 public class PropertySwitchPoints {
  40 
  41     private final Map<Object, WeakSwitchPointSet> switchPointMap = new HashMap<>();
  42 
  43     private final static SwitchPoint[] EMPTY_SWITCHPOINT_ARRAY = new SwitchPoint[0];
  44 
  45     // These counters are updated in debug mode
  46     private static LongAdder switchPointsAdded;
  47     private static LongAdder switchPointsInvalidated;
  48 
  49     static {
  50         if (Context.DEBUG) {
  51             switchPointsAdded = new LongAdder();
  52             switchPointsInvalidated = new LongAdder();
  53         }
  54     }
  55 
  56     /**
  57      * Copy constructor
  58      *
  59      * @param switchPoints Proto switchpoints to copy
  60      */
  61     private PropertySwitchPoints(final PropertySwitchPoints switchPoints) {
  62         if (switchPoints != null) {
  63             // We need to copy the nested weak sets in order to avoid concurrent modification issues, see JDK-8146274
  64             synchronized (switchPoints) {
  65                 for (final Map.Entry<Object, WeakSwitchPointSet> entry : switchPoints.switchPointMap.entrySet()) {
  66                     this.switchPointMap.put(entry.getKey(), new WeakSwitchPointSet(entry.getValue()));
  67                 }
  68             }
  69         }
  70     }
  71 
  72     /**
  73      * Return aggregate switchpoints added to all ProtoSwitchPoints
  74      * @return the number of switchpoints added
  75      */
  76     public static long getSwitchPointsAdded() {
  77         return switchPointsAdded.longValue();
  78     }
  79 
  80     /**
  81      * Return aggregate switchPointMap invalidated in all ProtoSwitchPoints
  82      * @return the number of switchpoints invalidated
  83      */
  84     public static long getSwitchPointsInvalidated() {
  85         return switchPointsInvalidated.longValue();
  86     }
  87 
  88     /**
  89      * Return number of property switchPoints added to a ScriptObject.
  90      * @param obj the object
  91      * @return the switchpoint count
  92      */
  93     public static int getSwitchPointCount(final ScriptObject obj) {
  94         return obj.getMap().getSwitchPointCount();
  95     }
  96 
  97     /**
  98      * Return the number of switchpoints added to this ProtoSwitchPoints instance.
  99      * @return the switchpoint count;
 100      */
 101     int getSwitchPointCount() {
 102         return switchPointMap.size();
 103     }
 104 
 105     /**
 106      * Add {@code switchPoint} to the switchpoints for for property {@code key}, creating
 107      * and returning a new {@code ProtoSwitchPoints} instance if the switchpoint was not already contained
 108      *
 109      * @param oldSwitchPoints the original PropertySwitchPoints instance. May be null
 110      * @param key the property key
 111      * @param switchPoint the switchpoint to be added
 112      * @return the new PropertySwitchPoints instance, or this instance if switchpoint was already contained
 113      */
 114     static PropertySwitchPoints addSwitchPoint(final PropertySwitchPoints oldSwitchPoints, final String key, final SwitchPoint switchPoint) {
 115         if (oldSwitchPoints == null || !oldSwitchPoints.contains(key, switchPoint)) {
 116             final PropertySwitchPoints newSwitchPoints = new PropertySwitchPoints(oldSwitchPoints);
 117             newSwitchPoints.add(key, switchPoint);
 118             return newSwitchPoints;
 119         }
 120         return oldSwitchPoints;
 121     }
 122 
 123     /**
 124      * Checks whether {@code switchPoint} is contained in {@code key}'s set.
 125      *
 126      * @param key the property key
 127      * @param switchPoint the switchPoint
 128      * @return true if switchpoint is already contained for key
 129      */
 130     private synchronized boolean contains(final String key, final SwitchPoint switchPoint) {
 131         final WeakSwitchPointSet set = this.switchPointMap.get(key);
 132         return set != null && set.contains(switchPoint);
 133     }
 134 
 135     private synchronized void add(final String key, final SwitchPoint switchPoint) {
 136         if (Context.DEBUG) {
 137             switchPointsAdded.increment();
 138         }
 139 
 140         WeakSwitchPointSet set = this.switchPointMap.get(key);
 141         if (set == null) {
 142             set = new WeakSwitchPointSet();
 143             this.switchPointMap.put(key, set);
 144         }
 145 
 146         set.add(switchPoint);
 147     }
 148 
 149     Set<SwitchPoint> getSwitchPoints(final Object key) {
 150         WeakSwitchPointSet switchPointSet = switchPointMap.get(key);
 151         if (switchPointSet != null) {
 152             return switchPointSet.elements();
 153         }
 154 
 155         return Collections.emptySet();
 156     }
 157 
 158     /**
 159      * Invalidate all switchpoints for the given property. This is called when that
 160      * property is created, deleted, or modified in a script object.
 161      *
 162      * @param prop The property to invalidate.
 163      */
 164     synchronized void invalidateProperty(final Property prop) {
 165         final WeakSwitchPointSet set = switchPointMap.get(prop.getKey());
 166         if (set != null) {
 167             if (Context.DEBUG) {
 168                 switchPointsInvalidated.add(set.size());
 169             }
 170             final SwitchPoint[] switchPoints = set.elements().toArray(EMPTY_SWITCHPOINT_ARRAY);
 171             SwitchPoint.invalidateAll(switchPoints);
 172             this.switchPointMap.remove(prop.getKey());
 173         }
 174     }
 175 
 176 
 177     /**
 178      * Invalidate all switchpoints except those defined in {@code map}. This is called
 179      * when the prototype of a script object is changed.
 180      *
 181      * @param map map of properties to exclude from invalidation
 182      */
 183     synchronized void invalidateInheritedProperties(final PropertyMap map) {
 184         for (final Map.Entry<Object, WeakSwitchPointSet> entry : switchPointMap.entrySet()) {
 185             if (map.findProperty(entry.getKey()) != null) {
 186                 continue;
 187             }
 188             if (Context.DEBUG) {
 189                 switchPointsInvalidated.add(entry.getValue().size());
 190             }
 191             final SwitchPoint[] switchPoints = entry.getValue().elements().toArray(EMPTY_SWITCHPOINT_ARRAY);
 192             SwitchPoint.invalidateAll(switchPoints);
 193         }
 194         switchPointMap.clear();
 195     }
 196 
 197     private static class WeakSwitchPointSet {
 198 
 199         private final WeakHashMap<SwitchPoint, Void> map;
 200 
 201         WeakSwitchPointSet() {
 202             map = new WeakHashMap<>();
 203         }
 204 
 205         WeakSwitchPointSet(final WeakSwitchPointSet set) {
 206             map = new WeakHashMap<>(set.map);
 207         }
 208 
 209         void add(final SwitchPoint switchPoint) {
 210             map.put(switchPoint, null);
 211         }
 212 
 213         boolean contains(final SwitchPoint switchPoint) {
 214             return map.containsKey(switchPoint);
 215         }
 216 
 217         Set<SwitchPoint> elements() {
 218             return map.keySet();
 219         }
 220 
 221         int size() {
 222             return map.size();
 223         }
 224 
 225     }
 226 }