1 /* 2 * Copyright (c) 2010, 2014, 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.util.Map; 29 import java.util.Set; 30 import java.util.WeakHashMap; 31 import java.util.concurrent.atomic.LongAdder; 32 33 /** 34 * Helper class to manage property listeners and notification. 35 */ 36 public class PropertyListeners { 37 38 private Map<Object, WeakPropertyMapSet> listeners; 39 40 // These counters are updated in debug mode 41 private static LongAdder listenersAdded; 42 private static LongAdder listenersRemoved; 43 44 static { 45 if (Context.DEBUG) { 46 listenersAdded = new LongAdder(); 47 listenersRemoved = new LongAdder(); 48 } 49 } 50 51 /** 52 * Copy constructor 53 * @param listener listener to copy 54 */ 55 PropertyListeners(final PropertyListeners listener) { 56 if (listener != null && listener.listeners != null) { 57 this.listeners = new WeakHashMap<>(); 58 // We need to copy the nested weak sets in order to avoid concurrent modification issues, see JDK-8146274 59 synchronized (listener) { 60 for (final Map.Entry<Object, WeakPropertyMapSet> entry : listener.listeners.entrySet()) { 61 this.listeners.put(entry.getKey(), new WeakPropertyMapSet(entry.getValue())); 62 } 63 } 64 } 65 } 66 67 /** 68 * Return aggregate listeners added to all PropertyListenerManagers 69 * @return the listenersAdded 70 */ 71 public static long getListenersAdded() { 72 return listenersAdded.longValue(); 73 } 74 75 /** 76 * Return aggregate listeners removed from all PropertyListenerManagers 77 * @return the listenersRemoved 78 */ 79 public static long getListenersRemoved() { 80 return listenersRemoved.longValue(); 81 } 82 83 /** 84 * Return number of listeners added to a ScriptObject. 85 * @param obj the object 86 * @return the listener count 87 */ 88 public static int getListenerCount(final ScriptObject obj) { 89 return obj.getMap().getListenerCount(); 90 } 91 92 /** 93 * Return the number of listeners added to this PropertyListeners instance. 94 * @return the listener count; 95 */ 96 public int getListenerCount() { 97 return listeners == null ? 0 : listeners.size(); 98 } 99 100 // Property listener management methods 101 102 /** 103 * Add {@code propertyMap} as property listener to {@code listeners} using key {@code key} by 104 * creating and returning a new {@code PropertyListeners} instance. 105 * 106 * @param listeners the original property listeners instance, may be null 107 * @param key the property key 108 * @param propertyMap the property map 109 * @return the new property map 110 */ 111 public static PropertyListeners addListener(final PropertyListeners listeners, final String key, final PropertyMap propertyMap) { 112 final PropertyListeners newListeners; 113 if (listeners == null || !listeners.containsListener(key, propertyMap)) { 114 newListeners = new PropertyListeners(listeners); 115 newListeners.addListener(key, propertyMap); 116 return newListeners; 117 } 118 return listeners; 119 } 120 121 /** 122 * Checks whether {@code propertyMap} is registered as listener with {@code key}. 123 * 124 * @param key the property key 125 * @param propertyMap the property map 126 * @return true if property map is registered with property key 127 */ 128 synchronized boolean containsListener(final String key, final PropertyMap propertyMap) { 129 if (listeners == null) { 130 return false; 131 } 132 final WeakPropertyMapSet set = listeners.get(key); 133 return set != null && set.contains(propertyMap); 134 } 135 136 /** 137 * Add a property listener to this object. 138 * 139 * @param propertyMap The property listener that is added. 140 */ 141 synchronized final void addListener(final String key, final PropertyMap propertyMap) { 142 if (Context.DEBUG) { 143 listenersAdded.increment(); 144 } 145 if (listeners == null) { 146 listeners = new WeakHashMap<>(); 147 } 148 149 WeakPropertyMapSet set = listeners.get(key); 150 if (set == null) { 151 set = new WeakPropertyMapSet(); 152 listeners.put(key, set); 153 } 154 if (!set.contains(propertyMap)) { 155 set.add(propertyMap); 156 } 157 } 158 159 /** 160 * A new property is being added. 161 * 162 * @param prop The new Property added. 163 */ 164 public synchronized void propertyAdded(final Property prop) { 165 if (listeners != null) { 166 final WeakPropertyMapSet set = listeners.get(prop.getKey()); 167 if (set != null) { 168 for (final PropertyMap propertyMap : set.elements()) { 169 propertyMap.propertyAdded(prop, false); 170 } 171 listeners.remove(prop.getKey()); 172 if (Context.DEBUG) { 173 listenersRemoved.increment(); 174 } 175 } 176 } 177 } 178 179 /** 180 * An existing property is being deleted. 181 * 182 * @param prop The property being deleted. 183 */ 184 public synchronized void propertyDeleted(final Property prop) { 185 if (listeners != null) { 186 final WeakPropertyMapSet set = listeners.get(prop.getKey()); 187 if (set != null) { 188 for (final PropertyMap propertyMap : set.elements()) { 189 propertyMap.propertyDeleted(prop, false); 190 } 191 listeners.remove(prop.getKey()); 192 if (Context.DEBUG) { 193 listenersRemoved.increment(); 194 } 195 } 196 } 197 } 198 199 /** 200 * An existing Property is being replaced with a new Property. 201 * 202 * @param oldProp The old property that is being replaced. 203 * @param newProp The new property that replaces the old property. 204 * 205 */ 206 public synchronized void propertyModified(final Property oldProp, final Property newProp) { 207 if (listeners != null) { 208 final WeakPropertyMapSet set = listeners.get(oldProp.getKey()); 209 if (set != null) { 210 for (final PropertyMap propertyMap : set.elements()) { 211 propertyMap.propertyModified(oldProp, newProp, false); 212 } 213 listeners.remove(oldProp.getKey()); 214 if (Context.DEBUG) { 215 listenersRemoved.increment(); 216 } 217 } 218 } 219 } 220 221 /** 222 * Callback for when a proto is changed 223 */ 224 public synchronized void protoChanged() { 225 if (listeners != null) { 226 for (final WeakPropertyMapSet set : listeners.values()) { 227 for (final PropertyMap propertyMap : set.elements()) { 228 propertyMap.protoChanged(false); 229 } 230 } 231 listeners.clear(); 232 } 233 } 234 235 private static class WeakPropertyMapSet { 236 237 private final WeakHashMap<PropertyMap, Boolean> map; 238 239 WeakPropertyMapSet() { 240 this.map = new WeakHashMap<>(); 241 } 242 243 WeakPropertyMapSet(final WeakPropertyMapSet set) { 244 this.map = new WeakHashMap<>(set.map); 245 } 246 247 void add(final PropertyMap propertyMap) { 248 map.put(propertyMap, Boolean.TRUE); 249 } 250 251 boolean contains(final PropertyMap propertyMap) { 252 return map.containsKey(propertyMap); 253 } 254 255 Set<PropertyMap> elements() { 256 return map.keySet(); 257 } 258 259 } 260 } | 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 } |