mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8190427: Test for JDK-8165198 fails intermittently because of GC
Reviewed-by: jlaskey, sundar
This commit is contained in:
parent
2564212f92
commit
c2a4b3a570
7 changed files with 315 additions and 430 deletions
|
@ -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());
|
||||
|
|
|
@ -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<Object, WeakPropertyMapSet> 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<Object, WeakPropertyMapSet> 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<PropertyMap, Boolean> 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<PropertyMap> elements() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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<Object>, 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<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;
|
||||
|
||||
|
@ -147,8 +144,8 @@ public class PropertyMap implements Iterable<Object>, 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<Object>, 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
|
||||
* @param switchPoint the switchpoint
|
||||
*/
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 property The changed property.
|
||||
*/
|
||||
public void propertyChanged(final Property property) {
|
||||
if (propertySwitchPoints != null) {
|
||||
propertySwitchPoints.invalidateProperty(property);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A new property is being added.
|
||||
*
|
||||
* @param property The new Property added.
|
||||
* @param isSelf was the property added to this map?
|
||||
* Method called when the prototype of an object using this property map is changed.
|
||||
*/
|
||||
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?
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.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<SwitchPoint> 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<Object>, 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<Object>, 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<Object>, 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<Object>, 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.
|
||||
*
|
||||
|
|
|
@ -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<Object, WeakSwitchPointSet> 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<Object, WeakSwitchPointSet> 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<SwitchPoint> 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<Object, WeakSwitchPointSet> 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<SwitchPoint, Void> 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<SwitchPoint> elements() {
|
||||
return map.keySet();
|
||||
}
|
||||
|
||||
int size() {
|
||||
return map.size();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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<SwitchPoint> switchPoints = new ArrayList<>();
|
||||
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 Set<SwitchPoint> 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 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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue