提交 b642e31e 编写于 作者: H hannesw

8134609: Allow constructors with same prototoype map to share the allocator map

Reviewed-by: attila, sundar
上级 92853b17
...@@ -627,7 +627,7 @@ public final class NativeJSAdapter extends ScriptObject { ...@@ -627,7 +627,7 @@ public final class NativeJSAdapter extends ScriptObject {
return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class, return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
func.createBound(this, new Object[] { name })), 0, Object.class), func.createBound(this, new Object[] { name })), 0, Object.class),
testJSAdaptor(adaptee, null, null, null), testJSAdaptor(adaptee, null, null, null),
adaptee.getProtoSwitchPoint(__call__, find.getOwner())); adaptee.getProtoSwitchPoints(__call__, find.getOwner()), null);
} }
} }
throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this)); throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
...@@ -698,7 +698,7 @@ public final class NativeJSAdapter extends ScriptObject { ...@@ -698,7 +698,7 @@ public final class NativeJSAdapter extends ScriptObject {
return new GuardedInvocation( return new GuardedInvocation(
methodHandle, methodHandle,
testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func), testJSAdaptor(adaptee, findData.getGetter(Object.class, INVALID_PROGRAM_POINT, null), findData.getOwner(), func),
adaptee.getProtoSwitchPoint(hook, findData.getOwner())); adaptee.getProtoSwitchPoints(hook, findData.getOwner()), null);
} }
} }
} }
...@@ -710,7 +710,7 @@ public final class NativeJSAdapter extends ScriptObject { ...@@ -710,7 +710,7 @@ public final class NativeJSAdapter extends ScriptObject {
final MethodHandle methodHandle = hook.equals(__put__) ? final MethodHandle methodHandle = hook.equals(__put__) ?
MH.asType(Lookup.EMPTY_SETTER, type) : MH.asType(Lookup.EMPTY_SETTER, type) :
Lookup.emptyGetter(type.returnType()); Lookup.emptyGetter(type.returnType());
return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoint(hook, null)); return new GuardedInvocation(methodHandle, testJSAdaptor(adaptee, null, null, null), adaptee.getProtoSwitchPoints(hook, null), null);
} }
} }
......
...@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; ...@@ -29,6 +29,7 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import java.io.Serializable; import java.io.Serializable;
import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.ref.WeakReference;
import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.ObjectClassGenerator; import jdk.nashorn.internal.codegen.ObjectClassGenerator;
...@@ -53,6 +54,9 @@ final public class AllocationStrategy implements Serializable { ...@@ -53,6 +54,9 @@ final public class AllocationStrategy implements Serializable {
/** lazily generated allocator */ /** lazily generated allocator */
private transient MethodHandle allocator; private transient MethodHandle allocator;
/** Last used allocator map */
private transient AllocatorMap lastMap;
/** /**
* Construct an allocation strategy with the given map and class name. * Construct an allocation strategy with the given map and class name.
* @param fieldCount number of fields in the allocated object * @param fieldCount number of fields in the allocated object
...@@ -71,11 +75,49 @@ final public class AllocationStrategy implements Serializable { ...@@ -71,11 +75,49 @@ final public class AllocationStrategy implements Serializable {
return allocatorClassName; return allocatorClassName;
} }
PropertyMap getAllocatorMap() { /**
// Create a new map for each function instance * Get the property map for the allocated object.
return PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0); * @param prototype the prototype object
* @return the property map
*/
synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
assert prototype != null;
final PropertyMap protoMap = prototype.getMap();
if (lastMap != null) {
if (!lastMap.hasSharedProtoMap()) {
if (lastMap.hasSamePrototype(prototype)) {
return lastMap.allocatorMap;
}
if (lastMap.hasSameProtoMap(protoMap) && lastMap.hasUnchangedProtoMap()) {
// Convert to shared prototype map. Allocated objects will use the same property map
// that can be used as long as none of the prototypes modify the shared proto map.
final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
final SharedPropertyMap sharedProtoMap = new SharedPropertyMap(protoMap);
allocatorMap.setSharedProtoMap(sharedProtoMap);
prototype.setMap(sharedProtoMap);
lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
return allocatorMap;
}
}
if (lastMap.hasValidSharedProtoMap() && lastMap.hasSameProtoMap(protoMap)) {
prototype.setMap(lastMap.getSharedProtoMap());
return lastMap.allocatorMap;
}
}
final PropertyMap allocatorMap = PropertyMap.newMap(null, getAllocatorClassName(), 0, fieldCount, 0);
lastMap = new AllocatorMap(prototype, protoMap, allocatorMap);
return allocatorMap;
} }
/**
* Allocate an object with the given property map
* @param map the property map
* @return the allocated object
*/
ScriptObject allocate(final PropertyMap map) { ScriptObject allocate(final PropertyMap map) {
try { try {
if (allocator == null) { if (allocator == null) {
...@@ -94,4 +136,43 @@ final public class AllocationStrategy implements Serializable { ...@@ -94,4 +136,43 @@ final public class AllocationStrategy implements Serializable {
public String toString() { public String toString() {
return "AllocationStrategy[fieldCount=" + fieldCount + "]"; return "AllocationStrategy[fieldCount=" + fieldCount + "]";
} }
static class AllocatorMap {
final private WeakReference<ScriptObject> prototype;
final private WeakReference<PropertyMap> prototypeMap;
private PropertyMap allocatorMap;
AllocatorMap(final ScriptObject prototype, final PropertyMap protoMap, final PropertyMap allocMap) {
this.prototype = new WeakReference<>(prototype);
this.prototypeMap = new WeakReference<>(protoMap);
this.allocatorMap = allocMap;
}
boolean hasSamePrototype(final ScriptObject proto) {
return prototype.get() == proto;
}
boolean hasSameProtoMap(final PropertyMap protoMap) {
return prototypeMap.get() == protoMap || allocatorMap.getSharedProtoMap() == protoMap;
}
boolean hasUnchangedProtoMap() {
final ScriptObject proto = prototype.get();
return proto != null && proto.getMap() == prototypeMap.get();
}
boolean hasSharedProtoMap() {
return getSharedProtoMap() != null;
}
boolean hasValidSharedProtoMap() {
return hasSharedProtoMap() && getSharedProtoMap().isValidSharedProtoMap();
}
PropertyMap getSharedProtoMap() {
return allocatorMap.getSharedProtoMap();
}
}
} }
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
package jdk.nashorn.internal.runtime; package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.parser.TokenType.EOF; import static jdk.nashorn.internal.parser.TokenType.EOF;
import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.internal.parser.Lexer; import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenStream; import jdk.nashorn.internal.parser.TokenStream;
...@@ -62,6 +64,15 @@ public final class Debug { ...@@ -62,6 +64,15 @@ public final class Debug {
return firstJSFrame(new Throwable()); return firstJSFrame(new Throwable());
} }
/**
* Return a formatted script stack trace string with frames information separated by '\n'.
* This is a shortcut for {@code NashornException.getScriptStackString(new Throwable())}.
* @return formatted stack trace string
*/
public static String scriptStack() {
return NashornException.getScriptStackString(new Throwable());
}
/** /**
* Return the system identity hashcode for an object as a human readable * Return the system identity hashcode for an object as a human readable
* string * string
......
...@@ -75,16 +75,20 @@ public class PropertyListeners { ...@@ -75,16 +75,20 @@ public class PropertyListeners {
} }
/** /**
* Return listeners added to this ScriptObject. * Return number of listeners added to a ScriptObject.
* @param obj the object * @param obj the object
* @return the listener count * @return the listener count
*/ */
public static int getListenerCount(final ScriptObject obj) { public static int getListenerCount(final ScriptObject obj) {
final PropertyListeners propertyListeners = obj.getMap().getListeners(); return obj.getMap().getListenerCount();
if (propertyListeners != null) { }
return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size();
} /**
return 0; * 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 // Property listener management methods
...@@ -156,7 +160,7 @@ public class PropertyListeners { ...@@ -156,7 +160,7 @@ public class PropertyListeners {
final WeakPropertyMapSet set = listeners.get(prop.getKey()); final WeakPropertyMapSet set = listeners.get(prop.getKey());
if (set != null) { if (set != null) {
for (final PropertyMap propertyMap : set.elements()) { for (final PropertyMap propertyMap : set.elements()) {
propertyMap.propertyAdded(prop); propertyMap.propertyAdded(prop, false);
} }
listeners.remove(prop.getKey()); listeners.remove(prop.getKey());
if (Context.DEBUG) { if (Context.DEBUG) {
...@@ -176,7 +180,7 @@ public class PropertyListeners { ...@@ -176,7 +180,7 @@ public class PropertyListeners {
final WeakPropertyMapSet set = listeners.get(prop.getKey()); final WeakPropertyMapSet set = listeners.get(prop.getKey());
if (set != null) { if (set != null) {
for (final PropertyMap propertyMap : set.elements()) { for (final PropertyMap propertyMap : set.elements()) {
propertyMap.propertyDeleted(prop); propertyMap.propertyDeleted(prop, false);
} }
listeners.remove(prop.getKey()); listeners.remove(prop.getKey());
if (Context.DEBUG) { if (Context.DEBUG) {
...@@ -198,7 +202,7 @@ public class PropertyListeners { ...@@ -198,7 +202,7 @@ public class PropertyListeners {
final WeakPropertyMapSet set = listeners.get(oldProp.getKey()); final WeakPropertyMapSet set = listeners.get(oldProp.getKey());
if (set != null) { if (set != null) {
for (final PropertyMap propertyMap : set.elements()) { for (final PropertyMap propertyMap : set.elements()) {
propertyMap.propertyModified(oldProp, newProp); propertyMap.propertyModified(oldProp, newProp, false);
} }
listeners.remove(oldProp.getKey()); listeners.remove(oldProp.getKey());
if (Context.DEBUG) { if (Context.DEBUG) {
...@@ -215,7 +219,7 @@ public class PropertyListeners { ...@@ -215,7 +219,7 @@ public class PropertyListeners {
if (listeners != null) { if (listeners != null) {
for (final WeakPropertyMapSet set : listeners.values()) { for (final WeakPropertyMapSet set : listeners.values()) {
for (final PropertyMap propertyMap : set.elements()) { for (final PropertyMap propertyMap : set.elements()) {
propertyMap.protoChanged(); propertyMap.protoChanged(false);
} }
} }
listeners.clear(); listeners.clear();
......
...@@ -368,8 +368,8 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp ...@@ -368,8 +368,8 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
} }
@Override @Override
PropertyMap getAllocatorMap() { PropertyMap getAllocatorMap(final ScriptObject prototype) {
return allocationStrategy.getAllocatorMap(); return allocationStrategy.getAllocatorMap(prototype);
} }
@Override @Override
......
...@@ -521,35 +521,39 @@ public class ScriptFunction extends ScriptObject { ...@@ -521,35 +521,39 @@ public class ScriptFunction extends ScriptObject {
assert !isBoundFunction(); // allocate never invoked on bound functions assert !isBoundFunction(); // allocate never invoked on bound functions
final ScriptObject object = data.allocate(getAllocatorMap()); final ScriptObject prototype = getAllocatorPrototype();
final ScriptObject object = data.allocate(getAllocatorMap(prototype));
if (object != null) { if (object != null) {
final Object prototype = getPrototype(); object.setInitialProto(prototype);
if (prototype instanceof ScriptObject) {
object.setInitialProto((ScriptObject) prototype);
}
if (object.getProto() == null) {
object.setInitialProto(getObjectPrototype());
}
} }
return object; return object;
} }
private PropertyMap getAllocatorMap() { /**
if (allocatorMap == null) { * Get the property map used by "allocate"
allocatorMap = data.getAllocatorMap(); * @param prototype actual prototype object
* @return property map
*/
private synchronized PropertyMap getAllocatorMap(final ScriptObject prototype) {
if (allocatorMap == null || allocatorMap.isInvalidSharedMapFor(prototype)) {
// The prototype map has changed since this function was last used as constructor.
// Get a new allocator map.
allocatorMap = data.getAllocatorMap(prototype);
} }
return allocatorMap; return allocatorMap;
} }
/** /**
* Return Object.prototype - used by "allocate" * Return the actual prototype used by "allocate"
* * @return allocator prototype
* @return Object.prototype
*/ */
protected final ScriptObject getObjectPrototype() { private ScriptObject getAllocatorPrototype() {
final Object prototype = getPrototype();
if (prototype instanceof ScriptObject) {
return (ScriptObject) prototype;
}
return Global.objectPrototype(); return Global.objectPrototype();
} }
...@@ -591,10 +595,10 @@ public class ScriptFunction extends ScriptObject { ...@@ -591,10 +595,10 @@ public class ScriptFunction extends ScriptObject {
* *
* @param newPrototype new prototype object * @param newPrototype new prototype object
*/ */
public final void setPrototype(final Object newPrototype) { public synchronized final void setPrototype(final Object newPrototype) {
if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) { if (newPrototype instanceof ScriptObject && newPrototype != this.prototype && allocatorMap != null) {
// Replace our current allocator map with one that is associated with the new prototype. // Unset allocator map to be replaced with one matching the new prototype.
allocatorMap = allocatorMap.changeProto((ScriptObject) newPrototype); allocatorMap = null;
} }
this.prototype = newPrototype; this.prototype = newPrototype;
} }
......
...@@ -389,9 +389,10 @@ public abstract class ScriptFunctionData implements Serializable { ...@@ -389,9 +389,10 @@ public abstract class ScriptFunctionData implements Serializable {
/** /**
* Get the property map to use for objects allocated by this function. * Get the property map to use for objects allocated by this function.
* *
* @param prototype the prototype of the allocated object
* @return the property map for allocated objects. * @return the property map for allocated objects.
*/ */
PropertyMap getAllocatorMap() { PropertyMap getAllocatorMap(final ScriptObject prototype) {
return null; return null;
} }
......
...@@ -809,9 +809,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -809,9 +809,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (deep) { if (deep) {
final ScriptObject myProto = getProto(); final ScriptObject myProto = getProto();
if (myProto != null) { final FindProperty find = myProto == null ? null : myProto.findProperty(key, true, start);
return myProto.findProperty(key, deep, start); // checkSharedProtoMap must be invoked after myProto.checkSharedProtoMap to propagate
} // shared proto invalidation up the prototype chain. It also must be invoked when prototype is null.
checkSharedProtoMap();
return find;
} }
return null; return null;
...@@ -832,7 +834,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -832,7 +834,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (deep) { if (deep) {
final ScriptObject myProto = getProto(); final ScriptObject myProto = getProto();
if (myProto != null) { if (myProto != null) {
return myProto.hasProperty(key, deep); return myProto.hasProperty(key, true);
} }
} }
...@@ -1258,11 +1260,8 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -1258,11 +1260,8 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
if (oldProto != newProto) { if (oldProto != newProto) {
proto = newProto; proto = newProto;
// Let current listeners know that the prototype has changed and set our map // Let current listeners know that the prototype has changed
final PropertyListeners listeners = getMap().getListeners(); getMap().protoChanged(true);
if (listeners != null) {
listeners.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));
} }
...@@ -1314,7 +1313,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -1314,7 +1313,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
} }
p = p.getProto(); p = p.getProto();
} }
setProto((ScriptObject)newProto); setProto((ScriptObject) newProto);
} else { } else {
throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto)); throw typeError("cant.set.proto.to.non.object", ScriptRuntime.safeToString(this), ScriptRuntime.safeToString(newProto));
} }
...@@ -2012,11 +2011,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2012,11 +2011,11 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
final ScriptObject owner = find.getOwner(); final ScriptObject owner = find.getOwner();
final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class; final Class<ClassCastException> exception = explicitInstanceOfCheck ? null : ClassCastException.class;
final SwitchPoint protoSwitchPoint; final SwitchPoint[] protoSwitchPoints;
if (mh == null) { if (mh == null) {
mh = Lookup.emptyGetter(returnType); mh = Lookup.emptyGetter(returnType);
protoSwitchPoint = getProtoSwitchPoint(name, owner); protoSwitchPoints = getProtoSwitchPoints(name, owner);
} else if (!find.isSelf()) { } else if (!find.isSelf()) {
assert mh.type().returnType().equals(returnType) : assert mh.type().returnType().equals(returnType) :
"return type mismatch for getter " + mh.type().returnType() + " != " + returnType; "return type mismatch for getter " + mh.type().returnType() + " != " + returnType;
...@@ -2024,12 +2023,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2024,12 +2023,12 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
// Add a filter that replaces the self object with the prototype owning the property. // Add a filter that replaces the self object with the prototype owning the property.
mh = addProtoFilter(mh, find.getProtoChainLength()); mh = addProtoFilter(mh, find.getProtoChainLength());
} }
protoSwitchPoint = getProtoSwitchPoint(name, owner); protoSwitchPoints = getProtoSwitchPoints(name, owner);
} else { } else {
protoSwitchPoint = null; protoSwitchPoints = null;
} }
final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception); final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoints, exception);
return inv.addSwitchPoint(findBuiltinSwitchPoint(name)); return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
} }
...@@ -2128,17 +2127,32 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2128,17 +2127,32 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
* @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 a SwitchPoint or null
*/ */
public final SwitchPoint getProtoSwitchPoint(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<>();
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(); final ScriptObject parent = obj.getProto();
parent.getMap().addListener(name, obj.getMap()); parent.getMap().addListener(name, obj.getMap());
final SwitchPoint sp = parent.getMap().getSharedProtoSwitchPoint();
if (sp != null && !sp.hasBeenInvalidated()) {
switchPoints.add(sp);
}
} }
return getMap().getSwitchPoint(name); switchPoints.add(getMap().getSwitchPoint(name));
return switchPoints.toArray(new SwitchPoint[switchPoints.size()]);
}
private void checkSharedProtoMap() {
// Check if our map has an expected shared prototype property map. If it has, make sure that
// the prototype map has not been invalidated, and that it does match the actual map of the prototype.
if (getMap().isInvalidSharedMapFor(getProto())) {
// Change our own map to one that does not assume a shared prototype map.
setMap(getMap().makeUnsharedCopy());
}
} }
/** /**
...@@ -2220,7 +2234,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2220,7 +2234,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
return new GuardedInvocation( return new GuardedInvocation(
Lookup.EMPTY_SETTER, Lookup.EMPTY_SETTER,
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
getProtoSwitchPoint(name, null), getProtoSwitchPoints(name, null),
explicitInstanceOfCheck ? null : ClassCastException.class); explicitInstanceOfCheck ? null : ClassCastException.class);
} }
...@@ -2366,7 +2380,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2366,7 +2380,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
find.getGetter(Object.class, INVALID_PROGRAM_POINT, request), find.getGetter(Object.class, INVALID_PROGRAM_POINT, request),
find.getProtoChainLength(), find.getProtoChainLength(),
func), func),
getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()), getProtoSwitchPoints(NO_SUCH_PROPERTY_NAME, find.getOwner()),
//TODO this doesn't need a ClassCastException as guard always checks script object //TODO this doesn't need a ClassCastException as guard always checks script object
null); null);
} }
...@@ -2438,7 +2452,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable { ...@@ -2438,7 +2452,7 @@ public abstract class ScriptObject implements PropertyAccess, Cloneable {
} }
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck), getProtoSwitchPoints(name, null),
explicitInstanceOfCheck ? null : ClassCastException.class); explicitInstanceOfCheck ? null : ClassCastException.class);
} }
......
...@@ -186,10 +186,7 @@ final class SetMethodCreator { ...@@ -186,10 +186,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);
final PropertyListeners listeners = map.getListeners(); map.propertyAdded(sm.property, true);
if (listeners != null) {
listeners.propertyAdded(sm.property);
}
return sm; return sm;
} }
...@@ -204,7 +201,7 @@ final class SetMethodCreator { ...@@ -204,7 +201,7 @@ final class SetMethodCreator {
//fast type specific setter //fast type specific setter
final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already final MethodHandle fastSetter = property.getSetter(type, newMap); //0 sobj, 1 value, slot folded for spill property already
//slow setter, that calls ScriptObject.set with appropraite type and key name //slow setter, that calls ScriptObject.set with appropriate type and key name
MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)]; MethodHandle slowSetter = ScriptObject.SET_SLOW[getAccessorTypeIndex(type)];
slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.getFlags(desc)); slowSetter = MH.insertArguments(slowSetter, 3, NashornCallSiteDescriptor.getFlags(desc));
slowSetter = MH.insertArguments(slowSetter, 1, name); slowSetter = MH.insertArguments(slowSetter, 1, name);
......
/*
* Copyright (c) 2015, 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;
/**
* This class represents a property map that can be shared among multiple prototype objects, allowing all inheriting
* top-level objects to also share one property map. This is class is only used for prototype objects, the
* top-level objects use ordinary {@link PropertyMap}s with the {@link PropertyMap#sharedProtoMap} field
* set to the expected shared prototype map.
*
* <p>When an instance of this class is evolved because a property is added, removed, or modified in an object
* using it, the {@link #invalidateSwitchPoint()} method is invoked to signal to all callsites and inheriting
* objects that the assumption of a single shared prototype map is no longer valid. The property map resulting
* from the modification will no longer be an instance of this class.</p>
*/
public final class SharedPropertyMap extends PropertyMap {
private SwitchPoint switchPoint;
private static final long serialVersionUID = 2166297719721778876L;
/**
* Create a new shared property map from the given {@code map}.
* @param map property map to copy
*/
public 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);
}
@Override
synchronized boolean isValidSharedProtoMap() {
return switchPoint != null;
}
@Override
synchronized SwitchPoint getSharedProtoSwitchPoint() {
return switchPoint;
}
/**
* Invalidate the shared prototype switch point if this is a shared prototype map.
*/
synchronized void invalidateSwitchPoint() {
if (switchPoint != null) {
assert !switchPoint.hasBeenInvalidated();
SwitchPoint.invalidateAll(new SwitchPoint[]{ switchPoint });
switchPoint = null;
}
}
}
...@@ -46,7 +46,7 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards; ...@@ -46,7 +46,7 @@ import jdk.nashorn.internal.runtime.linker.NashornGuards;
* *
*/ */
public final class WithObject extends Scope { public final class WithObject extends Scope {
private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint.class); private static final MethodHandle WITHEXPRESSIONGUARD = findOwnMH("withExpressionGuard", boolean.class, Object.class, PropertyMap.class, SwitchPoint[].class);
private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class); private static final MethodHandle WITHEXPRESSIONFILTER = findOwnMH("withFilterExpression", Object.class, Object.class);
private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class); private static final MethodHandle WITHSCOPEFILTER = findOwnMH("withFilterScope", Object.class, Object.class);
private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class); private static final MethodHandle BIND_TO_EXPRESSION_OBJ = findOwnMH("bindToExpression", Object.class, Object.class, Object.class);
...@@ -360,13 +360,24 @@ public final class WithObject extends Scope { ...@@ -360,13 +360,24 @@ public final class WithObject extends Scope {
private MethodHandle expressionGuard(final String name, final ScriptObject owner) { private MethodHandle expressionGuard(final String name, final ScriptObject owner) {
final PropertyMap map = expression.getMap(); final PropertyMap map = expression.getMap();
final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner); final SwitchPoint[] sp = expression.getProtoSwitchPoints(name, owner);
return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp); return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint sp) { private static boolean withExpressionGuard(final Object receiver, final PropertyMap map, final SwitchPoint[] sp) {
return ((WithObject)receiver).expression.getMap() == map && (sp == null || !sp.hasBeenInvalidated()); return ((WithObject)receiver).expression.getMap() == map && !hasBeenInvalidated(sp);
}
private static boolean hasBeenInvalidated(final SwitchPoint[] switchPoints) {
if (switchPoints != null) {
for (final SwitchPoint switchPoint : switchPoints) {
if (switchPoint.hasBeenInvalidated()) {
return true;
}
}
}
return false;
} }
/** /**
......
...@@ -62,67 +62,146 @@ function createDeep() { ...@@ -62,67 +62,146 @@ function createDeep() {
return new C(); return new C();
} }
function createDeeper() {
function C() {
this.i1 = 1;
this.i2 = 2;
this.i3 = 3;
return this;
}
function D() {
this.p1 = 1;
this.p2 = 2;
this.p3 = 3;
return this;
}
function E() {
this.e1 = 1;
this.e2 = 2;
this.e3 = 3;
return this;
}
D.prototype = new E();
C.prototype = new D();
return new C();
}
function createEval() { function createEval() {
return eval("Object.create({})"); return eval("Object.create({})");
} }
function p(o) { print(o.x) } function p(o) { print(o.x) }
var a, b; function e(o) { print(o.e1) }
var a, b, c;
create(); create();
a = create(); a = create();
b = create(); b = create();
c = create();
a.__proto__.x = 123; a.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
a = create(); a = create();
b = create(); b = create();
c = create();
b.__proto__.x = 123; b.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
a = createEmpty(); a = createEmpty();
b = createEmpty(); b = createEmpty();
c = createEmpty();
a.__proto__.x = 123; a.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
a = createEmpty(); a = createEmpty();
b = createEmpty(); b = createEmpty();
c = createEmpty();
b.__proto__.x = 123; b.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
a = createDeep(); a = createDeep();
b = createDeep(); b = createDeep();
c = createDeep();
a.__proto__.__proto__.x = 123; a.__proto__.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
a = createDeep(); a = createDeep();
b = createDeep(); b = createDeep();
c = createDeep();
b.__proto__.__proto__.x = 123; b.__proto__.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
a.__proto__.__proto__.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
b.__proto__.__proto__.__proto__.x = 123;
p(a);
p(b);
p(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
a.__proto__.__proto__ = null;
e(a);
e(b);
e(c);
a = createDeeper();
b = createDeeper();
c = createDeeper();
b.__proto__.__proto__ = null;
e(a);
e(b);
e(c);
a = createEval(); a = createEval();
b = createEval(); b = createEval();
c = createEval();
a.__proto__.x = 123; a.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
a = createEval(); a = createEval();
b = createEval(); b = createEval();
c = createEval();
b.__proto__.x = 123; b.__proto__.x = 123;
p(a); p(a);
p(b); p(b);
p(c);
123 123
undefined undefined
undefined undefined
undefined
123
undefined
123
undefined
undefined
undefined
123 123
undefined
123 123
undefined undefined
undefined undefined
undefined
123 123
undefined
123 123
undefined undefined
undefined undefined
undefined
123 123
undefined
undefined
1
1
1
undefined
1
123 123
undefined undefined
undefined undefined
undefined
123 123
undefined
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/**
* JDK-8134609: Allow constructors with same prototoype map to share the allocator map
*
* @test
* @run
* @fork
* @option -Dnashorn.debug
*/
function createProto(members) {
function P() {
for (var id in members) {
if (members.hasOwnProperty(id)) {
this[id] = members[id];
}
}
return this;
}
return new P();
}
function createSubclass(prototype, members) {
function C() {
for (var id in members) {
if (members.hasOwnProperty(id)) {
this[id] = members[id];
}
}
return this;
}
C.prototype = prototype;
return new C();
}
function assertP1(object, value) {
Assert.assertTrue(object.p1 === value);
}
// First prototype will have non-shared proto-map. Second and third will be shared.
var proto0 = createProto({p1: 0, p2: 1});
var proto1 = createProto({p1: 1, p2: 2});
var proto2 = createProto({p1: 2, p2: 3});
Assert.assertTrue(Debug.map(proto1) === Debug.map(proto2));
assertP1(proto1, 1);
assertP1(proto2, 2);
// First instantiation will have a non-shared prototype map, from the second one
// maps will be shared until a different proto map comes along.
var child0 = createSubclass(proto1, {c1: 1, c2: 2});
var child1 = createSubclass(proto2, {c1: 2, c2: 3});
var child2 = createSubclass(proto1, {c1: 3, c2: 4});
var child3 = createSubclass(proto2, {c1: 1, c2: 2});
var child4 = createSubclass(proto0, {c1: 3, c2: 2});
Assert.assertTrue(Debug.map(child1) === Debug.map(child2));
Assert.assertTrue(Debug.map(child1) === Debug.map(child3));
Assert.assertTrue(Debug.map(child3) !== Debug.map(child4));
assertP1(child1, 2);
assertP1(child2, 1);
assertP1(child3, 2);
assertP1(child4, 0);
Assert.assertTrue(delete proto2.p1);
assertP1(child3, undefined);
assertP1(child2, 1);
Assert.assertTrue(Debug.map(child1) !== Debug.map(child3));
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册