diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java index d894abb0cf5569a44c9fdba07c4e77ea7d76c1b4..4409c0bdaa910532951de45b94a2e05c79c9bd0c 100644 --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeDebug.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -36,7 +36,7 @@ import jdk.nashorn.internal.objects.annotations.ScriptClass; import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; -import jdk.nashorn.internal.runtime.PropertyListeners; +import jdk.nashorn.internal.runtime.PropertySwitchPoints; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.Scope; import jdk.nashorn.internal.runtime.ScriptFunction; @@ -244,7 +244,7 @@ public final class NativeDebug extends ScriptObject { */ @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static int getListenerCount(final Object self, final Object obj) { - return (obj instanceof ScriptObject) ? PropertyListeners.getListenerCount((ScriptObject) obj) : 0; + return (obj instanceof ScriptObject) ? PropertySwitchPoints.getSwitchPointCount((ScriptObject) obj) : 0; } /** @@ -260,8 +260,8 @@ public final class NativeDebug extends ScriptObject { out.println("ScriptObject count " + ScriptObject.getCount()); out.println("Scope count " + Scope.getScopeCount()); - out.println("ScriptObject listeners added " + PropertyListeners.getListenersAdded()); - out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved()); + out.println("Property SwitchPoints added " + PropertySwitchPoints.getSwitchPointsAdded()); + out.println("Property SwitchPoints invalidated " + PropertySwitchPoints.getSwitchPointsInvalidated()); out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount()); out.println("ScriptFunction invokes " + ScriptFunction.getInvokes()); out.println("ScriptFunction allocations " + ScriptFunction.getAllocations()); diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java deleted file mode 100644 index a6e5ec363fa645a99ec1c17a0ad029d1b6116427..0000000000000000000000000000000000000000 --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyListeners.java +++ /dev/null @@ -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(); - } - - } -} diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java index 55f2e78db7260b8a51d74ebb69d9747ec8860a61..366263854b416fd291bac234b48a0b6dfa815cd5 100644 --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -40,9 +40,9 @@ import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; -import java.util.HashMap; import java.util.Iterator; import java.util.NoSuchElementException; +import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.atomic.LongAdder; import jdk.nashorn.internal.runtime.options.Options; @@ -95,17 +95,14 @@ public class PropertyMap implements Iterable, Serializable { * property map should only be used if it the same as the actual prototype map. */ private transient SharedPropertyMap sharedProtoMap; - /** {@link SwitchPoint}s for gets on inherited properties. */ - private transient HashMap protoSwitches; - /** History of maps, used to limit map duplication. */ private transient WeakHashMap> history; /** History of prototypes, used to limit map duplication. */ private transient WeakHashMap> protoHistory; - /** property listeners */ - private transient PropertyListeners listeners; + /** SwitchPoints for properties inherited form this map */ + private transient PropertySwitchPoints propertySwitchPoints; private transient BitSet freeSlots; @@ -147,8 +144,8 @@ public class PropertyMap implements Iterable, Serializable { this.fieldCount = fieldCount; this.fieldMaximum = propertyMap.fieldMaximum; this.className = propertyMap.className; - // We inherit the parent property listeners instance. It will be cloned when a new listener is added. - this.listeners = propertyMap.listeners; + // We inherit the parent property propertySwitchPoints instance. It will be cloned when a new listener is added. + this.propertySwitchPoints = propertyMap.propertySwitchPoints; this.freeSlots = propertyMap.freeSlots; this.sharedProtoMap = propertyMap.sharedProtoMap; this.softReferenceDerivationLimit = softReferenceDerivationLimit; @@ -245,142 +242,70 @@ public class PropertyMap implements Iterable, Serializable { } /** - * Get the number of listeners of this map + * Get the number of property SwitchPoints of this map * - * @return the number of listeners + * @return the number of property SwitchPoints */ - public int getListenerCount() { - return listeners == null ? 0 : listeners.getListenerCount(); + public int getSwitchPointCount() { + return propertySwitchPoints == null ? 0 : propertySwitchPoints.getSwitchPointCount(); } /** - * Add {@code listenerMap} as a listener to this property map for the given {@code key}. + * Add a property switchpoint to this property map for the given {@code key}. * * @param key the property name - * @param listenerMap the listener map - */ - public void addListener(final String key, final PropertyMap listenerMap) { - if (listenerMap != this) { - // We need to clone listener instance when adding a new listener since we share - // the listeners instance with our parent maps that don't need to see the new listener. - listeners = PropertyListeners.addListener(listeners, key, listenerMap); - } - } - - /** - * A new property is being added. - * - * @param property The new Property added. - * @param isSelf was the property added to this map? - */ - public void propertyAdded(final Property property, final boolean isSelf) { - if (!isSelf) { - invalidateProtoSwitchPoint(property.getKey()); - } - if (listeners != null) { - listeners.propertyAdded(property); - } - } - - /** - * An existing property is being deleted. - * - * @param property The property being deleted. - * @param isSelf was the property deleted from this map? + * @param switchPoint the switchpoint */ - public void propertyDeleted(final Property property, final boolean isSelf) { - if (!isSelf) { - invalidateProtoSwitchPoint(property.getKey()); - } - if (listeners != null) { - listeners.propertyDeleted(property); - } + public void addSwitchPoint(final String key, final SwitchPoint switchPoint) { + // We need to clone listener instance when adding a new listener since we share + // the propertySwitchPoints instance with our parent maps that don't need to see the new listener. + propertySwitchPoints = PropertySwitchPoints.addSwitchPoint(propertySwitchPoints, key, switchPoint); } /** - * An existing property is being redefined. + * Method called when a property of an object using this property map is being created, + * modified, or deleted. If a switchpoint for the property exists it will be invalidated. * - * @param oldProperty The old property - * @param newProperty The new property - * @param isSelf was the property modified on this map? + * @param property The changed property. */ - public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) { - if (!isSelf) { - invalidateProtoSwitchPoint(oldProperty.getKey()); - } - if (listeners != null) { - listeners.propertyModified(oldProperty, newProperty); + public void propertyChanged(final Property property) { + if (propertySwitchPoints != null) { + propertySwitchPoints.invalidateProperty(property); } } /** - * The prototype of an object associated with this {@link PropertyMap} is changed. - * - * @param isSelf was the prototype changed on the object using this map? + * Method called when the prototype of an object using this property map is changed. */ - public void protoChanged(final boolean isSelf) { - if (!isSelf) { - invalidateAllProtoSwitchPoints(); - } else if (sharedProtoMap != null) { + void protoChanged() { + if (sharedProtoMap != null) { sharedProtoMap.invalidateSwitchPoint(); } - if (listeners != null) { - listeners.protoChanged(); + if (propertySwitchPoints != null) { + propertySwitchPoints.invalidateInheritedProperties(this); } } /** - * Return a SwitchPoint used to track changes of a property in a prototype. + * Returns a SwitchPoint for use with a property inherited from this or a parent map. + * If such a switchpoint exists, it will be invalidated when the property is modified + * in an object using this map. This method returns {@code null} if no swichpoint exists + * for the property. * * @param key Property key. - * @return A shared {@link SwitchPoint} for the property. + * @return A {@link SwitchPoint} for the property, or null. */ public synchronized SwitchPoint getSwitchPoint(final String key) { - if (protoSwitches == null) { - protoSwitches = new HashMap<>(); - } - - SwitchPoint switchPoint = protoSwitches.get(key); - if (switchPoint == null) { - switchPoint = new SwitchPoint(); - protoSwitches.put(key, switchPoint); - } - - return switchPoint; - } - - /** - * Indicate that a prototype property has changed. - * - * @param key {@link Property} key to invalidate. - */ - synchronized void invalidateProtoSwitchPoint(final Object key) { - if (protoSwitches != null) { - final SwitchPoint sp = protoSwitches.get(key); - if (sp != null) { - protoSwitches.remove(key); - if (Context.DEBUG) { - protoInvalidations.increment(); + if (propertySwitchPoints != null) { + final Set existingSwitchPoints = propertySwitchPoints.getSwitchPoints(key); + for (final SwitchPoint switchPoint : existingSwitchPoints) { + if (switchPoint != null && !switchPoint.hasBeenInvalidated()) { + return switchPoint; } - SwitchPoint.invalidateAll(new SwitchPoint[]{sp}); } } - } - /** - * Indicate that proto itself has changed in hierarchy somewhere. - */ - synchronized void invalidateAllProtoSwitchPoints() { - if (protoSwitches != null) { - final int size = protoSwitches.size(); - if (size > 0) { - if (Context.DEBUG) { - protoInvalidations.add(size); - } - SwitchPoint.invalidateAll(protoSwitches.values().toArray(new SwitchPoint[0])); - protoSwitches.clear(); - } - } + return null; } /** @@ -452,7 +377,7 @@ public class PropertyMap implements Iterable, Serializable { * @return New {@link PropertyMap} with {@link Property} added. */ public final PropertyMap addPropertyNoHistory(final Property property) { - propertyAdded(property, true); + propertyChanged(property); return addPropertyInternal(property); } @@ -464,7 +389,7 @@ public class PropertyMap implements Iterable, Serializable { * @return New {@link PropertyMap} with {@link Property} added. */ public final synchronized PropertyMap addProperty(final Property property) { - propertyAdded(property, true); + propertyChanged(property); PropertyMap newMap = checkHistory(property); if (newMap == null) { @@ -494,7 +419,7 @@ public class PropertyMap implements Iterable, Serializable { * @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found. */ public final synchronized PropertyMap deleteProperty(final Property property) { - propertyDeleted(property, true); + propertyChanged(property); PropertyMap newMap = checkHistory(property); final Object key = property.getKey(); @@ -529,7 +454,7 @@ public class PropertyMap implements Iterable, Serializable { * @return New {@link PropertyMap} with {@link Property} replaced. */ public final PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) { - propertyModified(oldProperty, newProperty, true); + propertyChanged(oldProperty); /* * See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods. * diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertySwitchPoints.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertySwitchPoints.java new file mode 100644 index 0000000000000000000000000000000000000000..f279124e92fd6c34550249c6c1f7ddb5a08618c7 --- /dev/null +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertySwitchPoints.java @@ -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(); + } + + } +} diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java index 3008114171fc57d2da062941a86c373e0289538a..19f1a2214e384b6da59515e60148c1818b1eb67f 100644 --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -1248,7 +1248,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { proto = newProto; // Let current listeners know that the prototype has changed - getMap().protoChanged(true); + getMap().protoChanged(); // Replace our current allocator map with one that is associated with the new prototype. setMap(getMap().changeProto(newProto)); } @@ -2107,30 +2107,38 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { } /** - * Get a switch point for a property with the given {@code name} that will be invalidated when - * the property definition is changed in this object's prototype chain. Returns {@code null} if - * the property is defined in this object itself. + * Get an array of switch points for a property with the given {@code name} that will be + * invalidated when the property definition is changed in this object's prototype chain. + * Returns {@code null} if the property is defined in this object itself. * * @param name the property name * @param owner the property owner, null if property is not defined - * @return a SwitchPoint or null + * @return an array of SwitchPoints or null */ public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) { if (owner == this || getProto() == null) { return null; } - final List switchPoints = new ArrayList<>(); + final Set switchPoints = new HashSet<>(); + SwitchPoint switchPoint = getProto().getMap().getSwitchPoint(name); + + if (switchPoint == null) { + switchPoint = new SwitchPoint(); + for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { + obj.getProto().getMap().addSwitchPoint(name, switchPoint); + } + } + + switchPoints.add(switchPoint); + for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) { - final ScriptObject parent = obj.getProto(); - parent.getMap().addListener(name, obj.getMap()); - final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint(); - if (sp != null && !sp.hasBeenInvalidated()) { - switchPoints.add(sp); + final SwitchPoint sharedProtoSwitchPoint = obj.getProto().getMap().getSharedProtoSwitchPoint(); + if (sharedProtoSwitchPoint != null && !sharedProtoSwitchPoint.hasBeenInvalidated()) { + switchPoints.add(sharedProtoSwitchPoint); } } - switchPoints.add(getMap().getSwitchPoint(name)); return switchPoints.toArray(new SwitchPoint[0]); } @@ -2141,12 +2149,16 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { return null; } - for (ScriptObject obj = this; obj.getProto() != null; obj = obj.getProto()) { - final ScriptObject parent = obj.getProto(); - parent.getMap().addListener(name, obj.getMap()); + SwitchPoint switchPoint = getProto().getMap().getSwitchPoint(name); + + if (switchPoint == null) { + switchPoint = new SwitchPoint(); + for (ScriptObject obj = this; obj.getProto() != null; obj = obj.getProto()) { + obj.getProto().getMap().addSwitchPoint(name, switchPoint); + } } - return getMap().getSwitchPoint(name); + return switchPoint; } private void checkSharedProtoMap() { diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java index 80bc2fe3e24488cfb5497371160c76ed151248c2..a71efb1173cd3701eb8669c408adfcb7d1d50427 100644 --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -185,7 +185,7 @@ final class SetMethodCreator { private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) { final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint); - map.propertyAdded(sm.property, true); + map.propertyChanged(sm.property); return sm; } diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SharedPropertyMap.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SharedPropertyMap.java index 5277c4faad49cb11863741d1946e65958532b558..aa029a90b1cdbf6d984c2532f036c9ad00886a68 100644 --- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SharedPropertyMap.java +++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SharedPropertyMap.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -48,33 +48,15 @@ public final class SharedPropertyMap extends PropertyMap { * Create a new shared property map from the given {@code map}. * @param map property map to copy */ - public SharedPropertyMap(final PropertyMap map) { + SharedPropertyMap(final PropertyMap map) { super(map); this.switchPoint = new SwitchPoint(); } @Override - public void propertyAdded(final Property property, final boolean isSelf) { - if (isSelf) { - invalidateSwitchPoint(); - } - super.propertyAdded(property, isSelf); - } - - @Override - public void propertyDeleted(final Property property, final boolean isSelf) { - if (isSelf) { - invalidateSwitchPoint(); - } - super.propertyDeleted(property, isSelf); - } - - @Override - public void propertyModified(final Property oldProperty, final Property newProperty, final boolean isSelf) { - if (isSelf) { - invalidateSwitchPoint(); - } - super.propertyModified(oldProperty, newProperty, isSelf); + public void propertyChanged(final Property property) { + invalidateSwitchPoint(); + super.propertyChanged(property); } @Override