/*
 * 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.
/** * 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
Params:
  • switchPoints – Proto switchpoints to copy
/** * 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
Returns:the number of switchpoints added
/** * 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
Returns:the number of switchpoints invalidated
/** * 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.
Params:
  • obj – the object
Returns:the switchpoint count
/** * 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.
Returns:the switchpoint count;
/** * Return the number of switchpoints added to this ProtoSwitchPoints instance. * @return the switchpoint count; */
int getSwitchPointCount() { return switchPointMap.size(); }
Add switchPoint to the switchpoints for for property key, creating and returning a new ProtoSwitchPoints instance if the switchpoint was not already contained
Params:
  • oldSwitchPoints – the original PropertySwitchPoints instance. May be null
  • key – the property key
  • switchPoint – the switchpoint to be added
Returns:the new PropertySwitchPoints instance, or this instance if switchpoint was already contained
/** * 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 switchPoint is contained in key's set.
Params:
  • key – the property key
  • switchPoint – the switchPoint
Returns:true if switchpoint is already contained for key
/** * 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.
Params:
  • prop – The property to invalidate.
/** * 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 map. This is called when the prototype of a script object is changed.
Params:
  • map – map of properties to exclude from invalidation
/** * 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(); } } }