< prev index next >
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/PropertyMap.java
Print this page
@@ -38,13 +38,14 @@
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
-import java.util.HashMap;
+import java.util.Collections;
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;
import jdk.nashorn.internal.scripts.JO;
@@ -93,21 +94,18 @@
/** A reference to the expected shared prototype property map. If this is set this
* 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<Object, SwitchPoint> protoSwitches;
-
/** History of maps, used to limit map duplication. */
private transient WeakHashMap<Property, Reference<PropertyMap>> history;
/** History of prototypes, used to limit map duplication. */
private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory;
- /** property listeners */
- private transient PropertyListeners listeners;
+ /** SwitchPoints for properties inherited form this map */
+ private transient PropertySwitchPoints propertySwitchPoints;
private transient BitSet freeSlots;
private static final long serialVersionUID = -7041836752008732533L;
@@ -145,12 +143,12 @@
this.flags = flags;
this.spillLength = spillLength;
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;
if (Context.DEBUG) {
@@ -243,146 +241,70 @@
public int size() {
return properties.size();
}
/**
- * 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
+ * @param switchPoint the switchpoint
*/
- public void addListener(final String key, final PropertyMap listenerMap) {
- if (listenerMap != this) {
+ public void addSwitchPoint(final String key, final SwitchPoint switchPoint) {
// 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);
- }
+ // 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 deleted.
+ * A property is about to change - invalidate all prototype switchpoints for the given property.
*
- * @param property The property being deleted.
- * @param isSelf was the property deleted from this map?
+ * @param property The modified property.
*/
- public void propertyDeleted(final Property property, final boolean isSelf) {
- if (!isSelf) {
- invalidateProtoSwitchPoint(property.getKey());
- }
- if (listeners != null) {
- listeners.propertyDeleted(property);
- }
- }
-
- /**
- * An existing property is being redefined.
- *
- * @param oldProperty The old property
- * @param newProperty The new property
- * @param isSelf was the property modified on this map?
- */
- 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 invalidateProperty(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?
*/
- 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.invalidateAll();
}
}
/**
- * Return a SwitchPoint used to track changes of a property in a prototype.
+ * Return a SwitchPoint for tracking a property in this map, if one exists.
*
* @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);
- }
-
+ if (propertySwitchPoints != null) {
+ final Set<SwitchPoint> existingSwitchPoints = propertySwitchPoints.getSwitchPoints(key);
+ for (final SwitchPoint switchPoint : existingSwitchPoints) {
+ if (switchPoint != null && !switchPoint.hasBeenInvalidated()) {
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();
- }
- 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;
}
/**
* Add a property to the map, re-binding its getters and setters,
* if available, to a given receiver. This is typically the global scope. See
@@ -450,11 +372,11 @@
*
* @param property {@link Property} being added.
* @return New {@link PropertyMap} with {@link Property} added.
*/
public final PropertyMap addPropertyNoHistory(final Property property) {
- propertyAdded(property, true);
+ invalidateProperty(property);
return addPropertyInternal(property);
}
/**
* Add a property to the map. Cloning or using an existing map if available.
@@ -462,11 +384,11 @@
* @param property {@link Property} being added.
*
* @return New {@link PropertyMap} with {@link Property} added.
*/
public final synchronized PropertyMap addProperty(final Property property) {
- propertyAdded(property, true);
+ invalidateProperty(property);
PropertyMap newMap = checkHistory(property);
if (newMap == null) {
newMap = addPropertyInternal(property);
addToHistory(property, newMap);
@@ -492,11 +414,11 @@
* @param property {@link Property} being removed.
*
* @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);
+ invalidateProperty(property);
PropertyMap newMap = checkHistory(property);
final Object key = property.getKey();
if (newMap == null && properties.containsKey(key)) {
final PropertyHashMap newProperties = properties.immutableRemove(key);
@@ -527,11 +449,11 @@
* @param newProperty New {@link Property}.
*
* @return New {@link PropertyMap} with {@link Property} replaced.
*/
public final PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
- propertyModified(oldProperty, newProperty, true);
+ invalidateProperty(oldProperty);
/*
* See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods.
*
* This replaceProperty method is called only for the following three cases:
*
< prev index next >