提交 1e523264 编写于 作者: H hannesw

8190427: Test for JDK-8165198 fails intermittently because of GC

Reviewed-by: jlaskey, sundar
上级 b219338b
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -36,7 +36,7 @@ import jdk.nashorn.internal.objects.annotations.ScriptClass; ...@@ -36,7 +36,7 @@ import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Where; import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.JSType; 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.PropertyMap;
import jdk.nashorn.internal.runtime.Scope; import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptFunction;
...@@ -244,7 +244,7 @@ public final class NativeDebug extends ScriptObject { ...@@ -244,7 +244,7 @@ public final class NativeDebug extends ScriptObject {
*/ */
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static int getListenerCount(final Object self, final Object obj) { 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 { ...@@ -260,8 +260,8 @@ public final class NativeDebug extends ScriptObject {
out.println("ScriptObject count " + ScriptObject.getCount()); out.println("ScriptObject count " + ScriptObject.getCount());
out.println("Scope count " + Scope.getScopeCount()); out.println("Scope count " + Scope.getScopeCount());
out.println("ScriptObject listeners added " + PropertyListeners.getListenersAdded()); out.println("Property SwitchPoints added " + PropertySwitchPoints.getSwitchPointsAdded());
out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved()); out.println("Property SwitchPoints invalidated " + PropertySwitchPoints.getSwitchPointsInvalidated());
out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount()); out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount());
out.println("ScriptFunction invokes " + ScriptFunction.getInvokes()); out.println("ScriptFunction invokes " + ScriptFunction.getInvokes());
out.println("ScriptFunction allocations " + ScriptFunction.getAllocations()); out.println("ScriptFunction allocations " + ScriptFunction.getAllocations());
......
/*
* 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();
}
}
}
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -40,9 +40,9 @@ import java.lang.ref.WeakReference; ...@@ -40,9 +40,9 @@ import java.lang.ref.WeakReference;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Set;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.atomic.LongAdder; import java.util.concurrent.atomic.LongAdder;
import jdk.nashorn.internal.runtime.options.Options; import jdk.nashorn.internal.runtime.options.Options;
...@@ -95,17 +95,14 @@ public class PropertyMap implements Iterable<Object>, Serializable { ...@@ -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. */ * property map should only be used if it the same as the actual prototype map. */
private transient SharedPropertyMap sharedProtoMap; 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. */ /** History of maps, used to limit map duplication. */
private transient WeakHashMap<Property, Reference<PropertyMap>> history; private transient WeakHashMap<Property, Reference<PropertyMap>> history;
/** History of prototypes, used to limit map duplication. */ /** History of prototypes, used to limit map duplication. */
private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory; private transient WeakHashMap<ScriptObject, SoftReference<PropertyMap>> protoHistory;
/** property listeners */ /** SwitchPoints for properties inherited form this map */
private transient PropertyListeners listeners; private transient PropertySwitchPoints propertySwitchPoints;
private transient BitSet freeSlots; private transient BitSet freeSlots;
...@@ -147,8 +144,8 @@ public class PropertyMap implements Iterable<Object>, Serializable { ...@@ -147,8 +144,8 @@ public class PropertyMap implements Iterable<Object>, Serializable {
this.fieldCount = fieldCount; this.fieldCount = fieldCount;
this.fieldMaximum = propertyMap.fieldMaximum; this.fieldMaximum = propertyMap.fieldMaximum;
this.className = propertyMap.className; this.className = propertyMap.className;
// We inherit the parent property listeners instance. It will be cloned when a new listener is added. // We inherit the parent property propertySwitchPoints instance. It will be cloned when a new listener is added.
this.listeners = propertyMap.listeners; this.propertySwitchPoints = propertyMap.propertySwitchPoints;
this.freeSlots = propertyMap.freeSlots; this.freeSlots = propertyMap.freeSlots;
this.sharedProtoMap = propertyMap.sharedProtoMap; this.sharedProtoMap = propertyMap.sharedProtoMap;
this.softReferenceDerivationLimit = softReferenceDerivationLimit; this.softReferenceDerivationLimit = softReferenceDerivationLimit;
...@@ -245,142 +242,70 @@ public class PropertyMap implements Iterable<Object>, Serializable { ...@@ -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() { public int getSwitchPointCount() {
return listeners == null ? 0 : listeners.getListenerCount(); 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 key the property name
* @param listenerMap the listener map * @param switchPoint the switchpoint
*/ */
public void addListener(final String key, final PropertyMap listenerMap) { public void addSwitchPoint(final String key, final SwitchPoint switchPoint) {
if (listenerMap != this) {
// We need to clone listener instance when adding a new listener since we share // 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. // the propertySwitchPoints instance with our parent maps that don't need to see the new listener.
listeners = PropertyListeners.addListener(listeners, key, listenerMap); propertySwitchPoints = PropertySwitchPoints.addSwitchPoint(propertySwitchPoints, key, switchPoint);
}
}
/**
* 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. * 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 property being deleted. * @param property The changed property.
* @param isSelf was the property deleted from this map?
*/ */
public void propertyDeleted(final Property property, final boolean isSelf) { public void propertyChanged(final Property property) {
if (!isSelf) { if (propertySwitchPoints != null) {
invalidateProtoSwitchPoint(property.getKey()); propertySwitchPoints.invalidateProperty(property);
}
if (listeners != null) {
listeners.propertyDeleted(property);
} }
} }
/** /**
* An existing property is being redefined. * Method called when the prototype of an object using this property map is changed.
*
* @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) { void protoChanged() {
if (!isSelf) { if (sharedProtoMap != null) {
invalidateAllProtoSwitchPoints();
} else if (sharedProtoMap != null) {
sharedProtoMap.invalidateSwitchPoint(); sharedProtoMap.invalidateSwitchPoint();
} }
if (listeners != null) { if (propertySwitchPoints != null) {
listeners.protoChanged(); 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. * @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) { public synchronized SwitchPoint getSwitchPoint(final String key) {
if (protoSwitches == null) { if (propertySwitchPoints != null) {
protoSwitches = new HashMap<>(); final Set<SwitchPoint> existingSwitchPoints = propertySwitchPoints.getSwitchPoints(key);
} for (final SwitchPoint switchPoint : existingSwitchPoints) {
if (switchPoint != null && !switchPoint.hasBeenInvalidated()) {
SwitchPoint switchPoint = protoSwitches.get(key);
if (switchPoint == null) {
switchPoint = new SwitchPoint();
protoSwitches.put(key, switchPoint);
}
return 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();
}
SwitchPoint.invalidateAll(new SwitchPoint[]{sp});
}
} }
} }
/** return null;
* 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();
}
}
} }
/** /**
...@@ -452,7 +377,7 @@ public class PropertyMap implements Iterable<Object>, Serializable { ...@@ -452,7 +377,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
* @return New {@link PropertyMap} with {@link Property} added. * @return New {@link PropertyMap} with {@link Property} added.
*/ */
public final PropertyMap addPropertyNoHistory(final Property property) { public final PropertyMap addPropertyNoHistory(final Property property) {
propertyAdded(property, true); propertyChanged(property);
return addPropertyInternal(property); return addPropertyInternal(property);
} }
...@@ -464,7 +389,7 @@ public class PropertyMap implements Iterable<Object>, Serializable { ...@@ -464,7 +389,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
* @return New {@link PropertyMap} with {@link Property} added. * @return New {@link PropertyMap} with {@link Property} added.
*/ */
public final synchronized PropertyMap addProperty(final Property property) { public final synchronized PropertyMap addProperty(final Property property) {
propertyAdded(property, true); propertyChanged(property);
PropertyMap newMap = checkHistory(property); PropertyMap newMap = checkHistory(property);
if (newMap == null) { if (newMap == null) {
...@@ -494,7 +419,7 @@ public class PropertyMap implements Iterable<Object>, Serializable { ...@@ -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. * @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found.
*/ */
public final synchronized PropertyMap deleteProperty(final Property property) { public final synchronized PropertyMap deleteProperty(final Property property) {
propertyDeleted(property, true); propertyChanged(property);
PropertyMap newMap = checkHistory(property); PropertyMap newMap = checkHistory(property);
final Object key = property.getKey(); final Object key = property.getKey();
...@@ -529,7 +454,7 @@ public class PropertyMap implements Iterable<Object>, Serializable { ...@@ -529,7 +454,7 @@ public class PropertyMap implements Iterable<Object>, Serializable {
* @return New {@link PropertyMap} with {@link Property} replaced. * @return New {@link PropertyMap} with {@link Property} replaced.
*/ */
public final PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) { public final PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
propertyModified(oldProperty, newProperty, true); propertyChanged(oldProperty);
/* /*
* See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods. * See ScriptObject.modifyProperty and ScriptObject.setUserAccessors methods.
* *
......
/*
* 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();
}
}
}
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -1248,7 +1248,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -1248,7 +1248,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
proto = newProto; proto = newProto;
// Let current listeners know that the prototype has changed // 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. // Replace our current allocator map with one that is associated with the new prototype.
setMap(getMap().changeProto(newProto)); setMap(getMap().changeProto(newProto));
} }
...@@ -2107,30 +2107,38 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -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 * Get an array of switch points for a property with the given {@code name} that will be
* the property definition is changed in this object's prototype chain. Returns {@code null} if * invalidated when the property definition is changed in this object's prototype chain.
* the property is defined in this object itself. * Returns {@code null} if the property is defined in this object itself.
* *
* @param name the property name * @param name the property name
* @param owner the property owner, null if property is not defined * @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) { public final SwitchPoint[] getProtoSwitchPoints(final String name, final ScriptObject owner) {
if (owner == this || getProto() == null) { if (owner == this || getProto() == null) {
return null; return null;
} }
final List<SwitchPoint> switchPoints = new ArrayList<>(); 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()) { for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
final ScriptObject parent = obj.getProto(); obj.getProto().getMap().addSwitchPoint(name, switchPoint);
parent.getMap().addListener(name, obj.getMap()); }
final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint(); }
if (sp != null && !sp.hasBeenInvalidated()) {
switchPoints.add(sp); 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]); return switchPoints.toArray(new SwitchPoint[0]);
} }
...@@ -2141,12 +2149,16 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2141,12 +2149,16 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return null; return null;
} }
SwitchPoint switchPoint = getProto().getMap().getSwitchPoint(name);
if (switchPoint == null) {
switchPoint = new SwitchPoint();
for (ScriptObject obj = this; obj.getProto() != null; obj = obj.getProto()) { for (ScriptObject obj = this; obj.getProto() != null; obj = obj.getProto()) {
final ScriptObject parent = obj.getProto(); obj.getProto().getMap().addSwitchPoint(name, switchPoint);
parent.getMap().addListener(name, obj.getMap()); }
} }
return getMap().getSwitchPoint(name); return switchPoint;
} }
private void checkSharedProtoMap() { private void checkSharedProtoMap() {
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -185,7 +185,7 @@ final class SetMethodCreator { ...@@ -185,7 +185,7 @@ final class SetMethodCreator {
private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) { private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) {
final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint); final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint);
map.propertyAdded(sm.property, true); map.propertyChanged(sm.property);
return sm; return sm;
} }
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -48,33 +48,15 @@ public final class SharedPropertyMap extends PropertyMap { ...@@ -48,33 +48,15 @@ public final class SharedPropertyMap extends PropertyMap {
* Create a new shared property map from the given {@code map}. * Create a new shared property map from the given {@code map}.
* @param map property map to copy * @param map property map to copy
*/ */
public SharedPropertyMap(final PropertyMap map) { SharedPropertyMap(final PropertyMap map) {
super(map); super(map);
this.switchPoint = new SwitchPoint(); this.switchPoint = new SwitchPoint();
} }
@Override @Override
public void propertyAdded(final Property property, final boolean isSelf) { public void propertyChanged(final Property property) {
if (isSelf) {
invalidateSwitchPoint(); invalidateSwitchPoint();
} super.propertyChanged(property);
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);
} }
@Override @Override
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册