--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java 2017-11-07 13:11:19.000000000 +0100 +++ /dev/null 2017-11-07 13:11:19.000000000 +0100 @@ -1,260 +0,0 @@ -/* - * Copyright (c) 2010, 2014, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package jdk.nashorn.internal.runtime; - -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.atomic.LongAdder; - -/** - * Helper class to manage property listeners and notification. - */ -public class PropertyListeners { - - private Map listeners; - - // These counters are updated in debug mode - private static LongAdder listenersAdded; - private static LongAdder listenersRemoved; - - static { - if (Context.DEBUG) { - listenersAdded = new LongAdder(); - listenersRemoved = new LongAdder(); - } - } - - /** - * Copy constructor - * @param listener listener to copy - */ - PropertyListeners(final PropertyListeners listener) { - if (listener != null && listener.listeners != null) { - this.listeners = new WeakHashMap<>(); - // We need to copy the nested weak sets in order to avoid concurrent modification issues, see JDK-8146274 - synchronized (listener) { - for (final Map.Entry entry : listener.listeners.entrySet()) { - this.listeners.put(entry.getKey(), new WeakPropertyMapSet(entry.getValue())); - } - } - } - } - - /** - * Return aggregate listeners added to all PropertyListenerManagers - * @return the listenersAdded - */ - public static long getListenersAdded() { - return listenersAdded.longValue(); - } - - /** - * Return aggregate listeners removed from all PropertyListenerManagers - * @return the listenersRemoved - */ - public static long getListenersRemoved() { - return listenersRemoved.longValue(); - } - - /** - * Return number of listeners added to a ScriptObject. - * @param obj the object - * @return the listener count - */ - public static int getListenerCount(final ScriptObject obj) { - return obj.getMap().getListenerCount(); - } - - /** - * Return the number of listeners added to this PropertyListeners instance. - * @return the listener count; - */ - public int getListenerCount() { - return listeners == null ? 0 : listeners.size(); - } - - // Property listener management methods - - /** - * Add {@code propertyMap} as property listener to {@code listeners} using key {@code key} by - * creating and returning a new {@code PropertyListeners} instance. - * - * @param listeners the original property listeners instance, may be null - * @param key the property key - * @param propertyMap the property map - * @return the new property map - */ - public static PropertyListeners addListener(final PropertyListeners listeners, final String key, final PropertyMap propertyMap) { - final PropertyListeners newListeners; - if (listeners == null || !listeners.containsListener(key, propertyMap)) { - newListeners = new PropertyListeners(listeners); - newListeners.addListener(key, propertyMap); - return newListeners; - } - return listeners; - } - - /** - * Checks whether {@code propertyMap} is registered as listener with {@code key}. - * - * @param key the property key - * @param propertyMap the property map - * @return true if property map is registered with property key - */ - synchronized boolean containsListener(final String key, final PropertyMap propertyMap) { - if (listeners == null) { - return false; - } - final WeakPropertyMapSet set = listeners.get(key); - return set != null && set.contains(propertyMap); - } - - /** - * Add a property listener to this object. - * - * @param propertyMap The property listener that is added. - */ - synchronized final void addListener(final String key, final PropertyMap propertyMap) { - if (Context.DEBUG) { - listenersAdded.increment(); - } - if (listeners == null) { - listeners = new WeakHashMap<>(); - } - - WeakPropertyMapSet set = listeners.get(key); - if (set == null) { - set = new WeakPropertyMapSet(); - listeners.put(key, set); - } - if (!set.contains(propertyMap)) { - set.add(propertyMap); - } - } - - /** - * A new property is being added. - * - * @param prop The new Property added. - */ - public synchronized void propertyAdded(final Property prop) { - if (listeners != null) { - final WeakPropertyMapSet set = listeners.get(prop.getKey()); - if (set != null) { - for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyAdded(prop, false); - } - listeners.remove(prop.getKey()); - if (Context.DEBUG) { - listenersRemoved.increment(); - } - } - } - } - - /** - * An existing property is being deleted. - * - * @param prop The property being deleted. - */ - public synchronized void propertyDeleted(final Property prop) { - if (listeners != null) { - final WeakPropertyMapSet set = listeners.get(prop.getKey()); - if (set != null) { - for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyDeleted(prop, false); - } - listeners.remove(prop.getKey()); - if (Context.DEBUG) { - listenersRemoved.increment(); - } - } - } - } - - /** - * An existing Property is being replaced with a new Property. - * - * @param oldProp The old property that is being replaced. - * @param newProp The new property that replaces the old property. - * - */ - public synchronized void propertyModified(final Property oldProp, final Property newProp) { - if (listeners != null) { - final WeakPropertyMapSet set = listeners.get(oldProp.getKey()); - if (set != null) { - for (final PropertyMap propertyMap : set.elements()) { - propertyMap.propertyModified(oldProp, newProp, false); - } - listeners.remove(oldProp.getKey()); - if (Context.DEBUG) { - listenersRemoved.increment(); - } - } - } - } - - /** - * Callback for when a proto is changed - */ - public synchronized void protoChanged() { - if (listeners != null) { - for (final WeakPropertyMapSet set : listeners.values()) { - for (final PropertyMap propertyMap : set.elements()) { - propertyMap.protoChanged(false); - } - } - listeners.clear(); - } - } - - private static class WeakPropertyMapSet { - - private final WeakHashMap map; - - WeakPropertyMapSet() { - this.map = new WeakHashMap<>(); - } - - WeakPropertyMapSet(final WeakPropertyMapSet set) { - this.map = new WeakHashMap<>(set.map); - } - - void add(final PropertyMap propertyMap) { - map.put(propertyMap, Boolean.TRUE); - } - - boolean contains(final PropertyMap propertyMap) { - return map.containsKey(propertyMap); - } - - Set elements() { - return map.keySet(); - } - - } -} --- /dev/null 2017-11-07 13:11:19.000000000 +0100 +++ new/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertySwitchPoints.java 2017-11-07 13:11:18.000000000 +0100 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2010, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.nashorn.internal.runtime; + +import java.lang.invoke.SwitchPoint; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.LongAdder; + +/** + * Helper class for tracking and invalidation of switchpoints for inherited properties. + */ +public class PropertySwitchPoints { + + private final Map switchPointMap = new HashMap<>(); + + private final static SwitchPoint[] EMPTY_SWITCHPOINT_ARRAY = new SwitchPoint[0]; + + // These counters are updated in debug mode + private static LongAdder switchPointsAdded; + private static LongAdder switchPointsInvalidated; + + static { + if (Context.DEBUG) { + switchPointsAdded = new LongAdder(); + switchPointsInvalidated = new LongAdder(); + } + } + + /** + * Copy constructor + * + * @param switchPoints Proto switchpoints to copy + */ + private PropertySwitchPoints(final PropertySwitchPoints switchPoints) { + if (switchPoints != null) { + // We need to copy the nested weak sets in order to avoid concurrent modification issues, see JDK-8146274 + synchronized (switchPoints) { + for (final Map.Entry entry : switchPoints.switchPointMap.entrySet()) { + this.switchPointMap.put(entry.getKey(), new WeakSwitchPointSet(entry.getValue())); + } + } + } + } + + /** + * Return aggregate switchpoints added to all ProtoSwitchPoints + * @return the number of switchpoints added + */ + public static long getSwitchPointsAdded() { + return switchPointsAdded.longValue(); + } + + /** + * Return aggregate switchPointMap invalidated in all ProtoSwitchPoints + * @return the number of switchpoints invalidated + */ + public static long getSwitchPointsInvalidated() { + return switchPointsInvalidated.longValue(); + } + + /** + * Return number of property switchPoints added to a ScriptObject. + * @param obj the object + * @return the switchpoint count + */ + public static int getSwitchPointCount(final ScriptObject obj) { + return obj.getMap().getSwitchPointCount(); + } + + /** + * Return the number of switchpoints added to this ProtoSwitchPoints instance. + * @return the switchpoint count; + */ + int getSwitchPointCount() { + return switchPointMap.size(); + } + + /** + * Add {@code switchPoint} to the switchpoints for for property {@code key}, creating + * and returning a new {@code ProtoSwitchPoints} instance if the switchpoint was not already contained + * + * @param oldSwitchPoints the original PropertySwitchPoints instance. May be null + * @param key the property key + * @param switchPoint the switchpoint to be added + * @return the new PropertySwitchPoints instance, or this instance if switchpoint was already contained + */ + static PropertySwitchPoints addSwitchPoint(final PropertySwitchPoints oldSwitchPoints, final String key, final SwitchPoint switchPoint) { + if (oldSwitchPoints == null || !oldSwitchPoints.contains(key, switchPoint)) { + final PropertySwitchPoints newSwitchPoints = new PropertySwitchPoints(oldSwitchPoints); + newSwitchPoints.add(key, switchPoint); + return newSwitchPoints; + } + return oldSwitchPoints; + } + + /** + * Checks whether {@code switchPoint} is contained in {@code key}'s set. + * + * @param key the property key + * @param switchPoint the switchPoint + * @return true if switchpoint is already contained for key + */ + private synchronized boolean contains(final String key, final SwitchPoint switchPoint) { + final WeakSwitchPointSet set = this.switchPointMap.get(key); + return set != null && set.contains(switchPoint); + } + + private synchronized void add(final String key, final SwitchPoint switchPoint) { + if (Context.DEBUG) { + switchPointsAdded.increment(); + } + + WeakSwitchPointSet set = this.switchPointMap.get(key); + if (set == null) { + set = new WeakSwitchPointSet(); + this.switchPointMap.put(key, set); + } + + set.add(switchPoint); + } + + Set getSwitchPoints(final Object key) { + WeakSwitchPointSet switchPointSet = switchPointMap.get(key); + if (switchPointSet != null) { + return switchPointSet.elements(); + } + + return Collections.emptySet(); + } + + /** + * Invalidate all switchpoints for the given property. This is called when that + * property is created, deleted, or modified in a script object. + * + * @param prop The property to invalidate. + */ + synchronized void invalidateProperty(final Property prop) { + final WeakSwitchPointSet set = switchPointMap.get(prop.getKey()); + if (set != null) { + if (Context.DEBUG) { + switchPointsInvalidated.add(set.size()); + } + final SwitchPoint[] switchPoints = set.elements().toArray(EMPTY_SWITCHPOINT_ARRAY); + SwitchPoint.invalidateAll(switchPoints); + this.switchPointMap.remove(prop.getKey()); + } + } + + + /** + * Invalidate all switchpoints except those defined in {@code map}. This is called + * when the prototype of a script object is changed. + * + * @param map map of properties to exclude from invalidation + */ + synchronized void invalidateInheritedProperties(final PropertyMap map) { + for (final Map.Entry entry : switchPointMap.entrySet()) { + if (map.findProperty(entry.getKey()) != null) { + continue; + } + if (Context.DEBUG) { + switchPointsInvalidated.add(entry.getValue().size()); + } + final SwitchPoint[] switchPoints = entry.getValue().elements().toArray(EMPTY_SWITCHPOINT_ARRAY); + SwitchPoint.invalidateAll(switchPoints); + } + switchPointMap.clear(); + } + + private static class WeakSwitchPointSet { + + private final WeakHashMap map; + + WeakSwitchPointSet() { + map = new WeakHashMap<>(); + } + + WeakSwitchPointSet(final WeakSwitchPointSet set) { + map = new WeakHashMap<>(set.map); + } + + void add(final SwitchPoint switchPoint) { + map.put(switchPoint, null); + } + + boolean contains(final SwitchPoint switchPoint) { + return map.containsKey(switchPoint); + } + + Set elements() { + return map.keySet(); + } + + int size() { + return map.size(); + } + + } +}