8035948: Redesign property listeners for shared classes
Reviewed-by: sundar, lagergren
--- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java Mon Mar 03 15:23:01 2014 +0100
@@ -50,8 +50,6 @@
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_EMPTY_LIST;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_SETISSHARED;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_SETISSHARED_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE;
@@ -191,8 +189,6 @@
// stack: Collection
// pmap = PropertyMap.newMap(Collection<Property>);
mi.invokeStatic(PROPERTYMAP_TYPE, PROPERTYMAP_NEWMAP, PROPERTYMAP_NEWMAP_DESC);
- // pmap.setIsShared();
- mi.invokeVirtual(PROPERTYMAP_TYPE, PROPERTYMAP_SETISSHARED, PROPERTYMAP_SETISSHARED_DESC);
// $nasgenmap$ = pmap;
mi.putStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
mi.returnVoid();
--- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java Mon Mar 03 15:23:01 2014 +0100
@@ -33,10 +33,7 @@
import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
@@ -171,9 +168,6 @@
private void loadMap(final MethodGenerator mi) {
if (memberCount > 0) {
mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
- // make sure we use duplicated PropertyMap so that original map
- // stays intact and so can be used for many globals.
- mi.invokeVirtual(PROPERTYMAP_TYPE, PROPERTYMAP_DUPLICATE, PROPERTYMAP_DUPLICATE_DESC);
}
}
--- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java Mon Mar 03 15:23:01 2014 +0100
@@ -32,10 +32,7 @@
import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DUPLICATE_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE_SUFFIX;
@@ -129,7 +126,6 @@
mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
// make sure we use duplicated PropertyMap so that original map
// stays intact and so can be used for many global.
- mi.invokeVirtual(PROPERTYMAP_TYPE, PROPERTYMAP_DUPLICATE, PROPERTYMAP_DUPLICATE_DESC);
mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC);
// initialize Function type fields
initFunctionFields(mi);
--- a/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java Mon Mar 03 15:23:01 2014 +0100
@@ -45,11 +45,9 @@
@SuppressWarnings("javadoc")
public interface StringConstants {
// standard jdk types, methods
- static final Type TYPE_METHOD = Type.getType(Method.class);
static final Type TYPE_METHODHANDLE = Type.getType(MethodHandle.class);
static final Type TYPE_METHODHANDLE_ARRAY = Type.getType(MethodHandle[].class);
static final Type TYPE_OBJECT = Type.getType(Object.class);
- static final Type TYPE_CLASS = Type.getType(Class.class);
static final Type TYPE_STRING = Type.getType(String.class);
static final Type TYPE_COLLECTION = Type.getType(Collection.class);
static final Type TYPE_COLLECTIONS = Type.getType(Collections.class);
@@ -104,10 +102,6 @@
static final String PROPERTYMAP_DESC = TYPE_PROPERTYMAP.getDescriptor();
static final String PROPERTYMAP_NEWMAP = "newMap";
static final String PROPERTYMAP_NEWMAP_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_COLLECTION);
- static final String PROPERTYMAP_DUPLICATE = "duplicate";
- static final String PROPERTYMAP_DUPLICATE_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP);
- static final String PROPERTYMAP_SETISSHARED = "setIsShared";
- static final String PROPERTYMAP_SETISSHARED_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP);
// PrototypeObject
static final String PROTOTYPEOBJECT_TYPE = TYPE_PROTOTYPEOBJECT.getInternalName();
--- a/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java Mon Mar 03 15:23:01 2014 +0100
@@ -25,6 +25,9 @@
package jdk.nashorn.internal.codegen;
+import jdk.nashorn.internal.runtime.Property;
+import jdk.nashorn.internal.runtime.PropertyMap;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -110,6 +113,43 @@
}
/**
+ * {@link PropertyMap} wrapper class that provides implementations for the {@code hashCode} and {@code equals}
+ * methods that are based on the map layout. {@code PropertyMap} itself inherits the identity based implementations
+ * from {@code java.lang.Object}.
+ */
+ private static class PropertyMapWrapper {
+ private final PropertyMap propertyMap;
+ private final int hashCode;
+
+ public PropertyMapWrapper(final PropertyMap map) {
+ int hash = 0;
+ for (final Property property : map.getProperties()) {
+ hash = hash << 7 ^ hash >> 7;
+ hash ^= property.hashCode();
+ }
+ this.hashCode = hash;
+ this.propertyMap = map;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(final Object other) {
+ if (!(other instanceof PropertyMapWrapper)) {
+ return false;
+ }
+
+ final Property[] ownProperties = propertyMap.getProperties();
+ final Property[] otherProperties = ((PropertyMapWrapper) other).propertyMap.getProperties();
+
+ return Arrays.equals(ownProperties, otherProperties);
+ }
+ }
+
+ /**
* Constructor
*/
ConstantData() {
@@ -145,7 +185,14 @@
* @return the index in the constant pool that the object was given
*/
public int add(final Object object) {
- final Object entry = object.getClass().isArray() ? new ArrayWrapper(object) : object;
+ final Object entry;
+ if (object.getClass().isArray()) {
+ entry = new ArrayWrapper(object);
+ } else if (object instanceof PropertyMap) {
+ entry = new PropertyMapWrapper((PropertyMap) object);
+ } else {
+ entry = object;
+ }
final Integer value = objectMap.get(entry);
if (value != null) {
--- a/nashorn/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java Mon Mar 03 15:23:01 2014 +0100
@@ -72,7 +72,7 @@
}
AccessorPropertyDescriptor(final boolean configurable, final boolean enumerable, final Object get, final Object set, final Global global) {
- super(global.getObjectPrototype(), global.getAccessorPropertyDescriptorMap());
+ super(global.getObjectPrototype(), getInitialMap());
this.configurable = configurable;
this.enumerable = enumerable;
this.get = get;
--- a/nashorn/src/jdk/nashorn/internal/objects/ArrayBufferView.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ArrayBufferView.java Mon Mar 03 15:23:01 2014 +0100
@@ -47,7 +47,7 @@
}
private ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength, final Global global) {
- super(global.getArrayBufferViewMap());
+ super(getInitialMap());
checkConstructorArgs(buffer, byteOffset, elementLength);
this.setProto(getPrototype(global));
this.setArray(factory().createArrayData(buffer, byteOffset, elementLength));
--- a/nashorn/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java Mon Mar 03 15:23:01 2014 +0100
@@ -69,7 +69,7 @@
}
DataPropertyDescriptor(final boolean configurable, final boolean enumerable, final boolean writable, final Object value, final Global global) {
- super(global.getObjectPrototype(), global.getDataPropertyDescriptorMap());
+ super(global.getObjectPrototype(), getInitialMap());
this.configurable = configurable;
this.enumerable = enumerable;
this.writable = writable;
--- a/nashorn/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java Mon Mar 03 15:23:01 2014 +0100
@@ -60,7 +60,7 @@
}
GenericPropertyDescriptor(final boolean configurable, final boolean enumerable, final Global global) {
- super(global.getObjectPrototype(), global.getGenericPropertyDescriptorMap());
+ super(global.getObjectPrototype(), getInitialMap());
this.configurable = configurable;
this.enumerable = enumerable;
}
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java Mon Mar 03 15:23:01 2014 +0100
@@ -370,36 +370,6 @@
*/
private ScriptFunction typeErrorThrower;
- private PropertyMap accessorPropertyDescriptorMap;
- private PropertyMap arrayBufferViewMap;
- private PropertyMap dataPropertyDescriptorMap;
- private PropertyMap genericPropertyDescriptorMap;
- private PropertyMap nativeArgumentsMap;
- private PropertyMap nativeArrayMap;
- private PropertyMap nativeArrayBufferMap;
- private PropertyMap nativeBooleanMap;
- private PropertyMap nativeDateMap;
- private PropertyMap nativeErrorMap;
- private PropertyMap nativeEvalErrorMap;
- private PropertyMap nativeJSAdapterMap;
- private PropertyMap nativeJavaImporterMap;
- private PropertyMap nativeNumberMap;
- private PropertyMap nativeRangeErrorMap;
- private PropertyMap nativeReferenceErrorMap;
- private PropertyMap nativeRegExpMap;
- private PropertyMap nativeRegExpExecResultMap;
- private PropertyMap nativeStrictArgumentsMap;
- private PropertyMap nativeStringMap;
- private PropertyMap nativeSyntaxErrorMap;
- private PropertyMap nativeTypeErrorMap;
- private PropertyMap nativeURIErrorMap;
- private PropertyMap prototypeObjectMap;
- private PropertyMap objectMap;
- private PropertyMap functionMap;
- private PropertyMap anonymousFunctionMap;
- private PropertyMap strictFunctionMap;
- private PropertyMap boundFunctionMap;
-
// Flag to indicate that a split method issued a return statement
private int splitState = -1;
@@ -557,7 +527,7 @@
@Override
public ScriptObject newObject() {
- return new JO(getObjectPrototype(), getObjectMap());
+ return new JO(getObjectPrototype(), JO.getInitialMap());
}
@Override
@@ -999,123 +969,6 @@
return ScriptFunction.getPrototype(builtinFloat64Array);
}
- // Builtin PropertyMap accessors
- PropertyMap getAccessorPropertyDescriptorMap() {
- return accessorPropertyDescriptorMap;
- }
-
- PropertyMap getArrayBufferViewMap() {
- return arrayBufferViewMap;
- }
-
- PropertyMap getDataPropertyDescriptorMap() {
- return dataPropertyDescriptorMap;
- }
-
- PropertyMap getGenericPropertyDescriptorMap() {
- return genericPropertyDescriptorMap;
- }
-
- PropertyMap getArgumentsMap() {
- return nativeArgumentsMap;
- }
-
- PropertyMap getArrayMap() {
- return nativeArrayMap;
- }
-
- PropertyMap getArrayBufferMap() {
- return nativeArrayBufferMap;
- }
-
- PropertyMap getBooleanMap() {
- return nativeBooleanMap;
- }
-
- PropertyMap getDateMap() {
- return nativeDateMap;
- }
-
- PropertyMap getErrorMap() {
- return nativeErrorMap;
- }
-
- PropertyMap getEvalErrorMap() {
- return nativeEvalErrorMap;
- }
-
- PropertyMap getJSAdapterMap() {
- return nativeJSAdapterMap;
- }
-
- PropertyMap getJavaImporterMap() {
- return nativeJavaImporterMap;
- }
-
- PropertyMap getNumberMap() {
- return nativeNumberMap;
- }
-
- PropertyMap getRangeErrorMap() {
- return nativeRangeErrorMap;
- }
-
- PropertyMap getReferenceErrorMap() {
- return nativeReferenceErrorMap;
- }
-
- PropertyMap getRegExpMap() {
- return nativeRegExpMap;
- }
-
- PropertyMap getRegExpExecResultMap() {
- return nativeRegExpExecResultMap;
- }
-
- PropertyMap getStrictArgumentsMap() {
- return nativeStrictArgumentsMap;
- }
-
- PropertyMap getStringMap() {
- return nativeStringMap;
- }
-
- PropertyMap getSyntaxErrorMap() {
- return nativeSyntaxErrorMap;
- }
-
- PropertyMap getTypeErrorMap() {
- return nativeTypeErrorMap;
- }
-
- PropertyMap getURIErrorMap() {
- return nativeURIErrorMap;
- }
-
- PropertyMap getPrototypeObjectMap() {
- return prototypeObjectMap;
- }
-
- PropertyMap getObjectMap() {
- return objectMap;
- }
-
- PropertyMap getFunctionMap() {
- return functionMap;
- }
-
- PropertyMap getAnonymousFunctionMap() {
- return anonymousFunctionMap;
- }
-
- PropertyMap getStrictFunctionMap() {
- return strictFunctionMap;
- }
-
- PropertyMap getBoundFunctionMap() {
- return boundFunctionMap;
- }
-
private ScriptFunction getBuiltinArray() {
return builtinArray;
}
@@ -1631,14 +1484,11 @@
final ScriptEnvironment env = getContext().getEnv();
- // duplicate PropertyMaps of Native* classes
- copyInitialMaps(env);
-
// initialize Function and Object constructor
initFunctionAndObject();
// Now fix Global's own proto.
- this.setProto(getObjectPrototype());
+ this.setInitialProto(getObjectPrototype());
// initialize global function properties
this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);
@@ -1783,7 +1633,7 @@
final ScriptObject prototype = ScriptFunction.getPrototype(cons);
prototype.set(NativeError.NAME, name, false);
prototype.set(NativeError.MESSAGE, "", false);
- prototype.setProto(errorProto);
+ prototype.setInitialProto(errorProto);
return (ScriptFunction)cons;
}
@@ -1955,7 +1805,7 @@
}
if (res.getProto() == null) {
- res.setProto(getObjectPrototype());
+ res.setInitialProto(getObjectPrototype());
}
return res;
@@ -1965,46 +1815,6 @@
}
}
- private void copyInitialMaps(final ScriptEnvironment env) {
- this.accessorPropertyDescriptorMap = AccessorPropertyDescriptor.getInitialMap().duplicate();
- this.dataPropertyDescriptorMap = DataPropertyDescriptor.getInitialMap().duplicate();
- this.genericPropertyDescriptorMap = GenericPropertyDescriptor.getInitialMap().duplicate();
- this.nativeArgumentsMap = NativeArguments.getInitialMap().duplicate();
- this.nativeArrayMap = NativeArray.getInitialMap().duplicate();
- this.nativeBooleanMap = NativeBoolean.getInitialMap().duplicate();
- this.nativeDateMap = NativeDate.getInitialMap().duplicate();
- this.nativeErrorMap = NativeError.getInitialMap().duplicate();
- this.nativeEvalErrorMap = NativeEvalError.getInitialMap().duplicate();
- this.nativeJSAdapterMap = NativeJSAdapter.getInitialMap().duplicate();
- this.nativeNumberMap = NativeNumber.getInitialMap().duplicate();
- this.nativeRangeErrorMap = NativeRangeError.getInitialMap().duplicate();
- this.nativeReferenceErrorMap = NativeReferenceError.getInitialMap().duplicate();
- this.nativeRegExpMap = NativeRegExp.getInitialMap().duplicate();
- this.nativeRegExpExecResultMap = NativeRegExpExecResult.getInitialMap().duplicate();
- this.nativeStrictArgumentsMap = NativeStrictArguments.getInitialMap().duplicate();
- this.nativeStringMap = NativeString.getInitialMap().duplicate();
- this.nativeSyntaxErrorMap = NativeSyntaxError.getInitialMap().duplicate();
- this.nativeTypeErrorMap = NativeTypeError.getInitialMap().duplicate();
- this.nativeURIErrorMap = NativeURIError.getInitialMap().duplicate();
- this.prototypeObjectMap = PrototypeObject.getInitialMap().duplicate();
- this.objectMap = JO.getInitialMap().duplicate();
- this.functionMap = ScriptFunctionImpl.getInitialMap().duplicate();
- this.anonymousFunctionMap = ScriptFunctionImpl.getInitialAnonymousMap().duplicate();
- this.strictFunctionMap = ScriptFunctionImpl.getInitialStrictMap().duplicate();
- this.boundFunctionMap = ScriptFunctionImpl.getInitialBoundMap().duplicate();
-
- // java
- if (! env._no_java) {
- this.nativeJavaImporterMap = NativeJavaImporter.getInitialMap().duplicate();
- }
-
- // typed arrays
- if (! env._no_typed_arrays) {
- this.arrayBufferViewMap = ArrayBufferView.getInitialMap().duplicate();
- this.nativeArrayBufferMap = NativeArrayBuffer.getInitialMap().duplicate();
- }
- }
-
// Function and Object constructors are inter-dependent. Also,
// Function.prototype
// functions are not properly initialized. We fix the references here.
@@ -2022,7 +1832,7 @@
// Function.prototype === Object.getPrototypeOf(Function) ===
// <anon-function>
- builtinFunction.setProto(anon);
+ builtinFunction.setInitialProto(anon);
builtinFunction.setPrototype(anon);
anon.set("constructor", builtinFunction, false);
anon.deleteOwnProperty(anon.getMap().findProperty("prototype"));
@@ -2038,7 +1848,7 @@
this.builtinObject = (ScriptFunction)initConstructor("Object");
final ScriptObject ObjectPrototype = getObjectPrototype();
// Object.getPrototypeOf(Function.prototype) === Object.prototype
- anon.setProto(ObjectPrototype);
+ anon.setInitialProto(ObjectPrototype);
// Function valued properties of Function.prototype were not properly
// initialized. Because, these were created before global.function and
@@ -2050,10 +1860,10 @@
if (value instanceof ScriptFunction && value != anon) {
final ScriptFunction func = (ScriptFunction)value;
- func.setProto(getFunctionPrototype());
+ func.setInitialProto(getFunctionPrototype());
final ScriptObject prototype = ScriptFunction.getPrototype(func);
if (prototype != null) {
- prototype.setProto(ObjectPrototype);
+ prototype.setInitialProto(ObjectPrototype);
}
}
}
@@ -2068,7 +1878,7 @@
final ScriptFunction func = (ScriptFunction)value;
final ScriptObject prototype = ScriptFunction.getPrototype(func);
if (prototype != null) {
- prototype.setProto(ObjectPrototype);
+ prototype.setInitialProto(ObjectPrototype);
}
}
}
@@ -2086,7 +1896,7 @@
final ScriptFunction func = (ScriptFunction)value;
final ScriptObject prototype = ScriptFunction.getPrototype(func);
if (prototype != null) {
- prototype.setProto(ObjectPrototype);
+ prototype.setInitialProto(ObjectPrototype);
}
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArguments.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArguments.java Mon Mar 03 15:23:01 2014 +0100
@@ -68,7 +68,7 @@
final ArrayList<Property> properties = new ArrayList<>(2);
properties.add(AccessorProperty.create("length", Property.NOT_ENUMERABLE, G$LENGTH, S$LENGTH));
properties.add(AccessorProperty.create("callee", Property.NOT_ENUMERABLE, G$CALLEE, S$CALLEE));
- map$ = PropertyMap.newMap(properties).setIsShared();
+ map$ = PropertyMap.newMap(properties);
}
static PropertyMap getInitialMap() {
@@ -267,9 +267,9 @@
final Global global = Global.instance();
final ScriptObject proto = global.getObjectPrototype();
if (isStrict) {
- return new NativeStrictArguments(arguments, numParams, proto, global.getStrictArgumentsMap());
+ return new NativeStrictArguments(arguments, numParams, proto, NativeStrictArguments.getInitialMap());
}
- return new NativeArguments(arguments, callee, numParams, proto, global.getArgumentsMap());
+ return new NativeArguments(arguments, callee, numParams, proto, NativeArguments.getInitialMap());
}
/**
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArray.java Mon Mar 03 15:23:01 2014 +0100
@@ -208,7 +208,7 @@
}
NativeArray(final ArrayData arrayData, final Global global) {
- super(global.getArrayPrototype(), global.getArrayMap());
+ super(global.getArrayPrototype(), getInitialMap());
this.setArray(arrayData);
this.setIsArray();
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java Mon Mar 03 15:23:01 2014 +0100
@@ -57,7 +57,7 @@
}
protected NativeArrayBuffer(final byte[] byteArray, final Global global) {
- super(global.getArrayBufferPrototype(), global.getArrayBufferMap());
+ super(global.getArrayBufferPrototype(), getInitialMap());
this.buffer = byteArray;
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java Mon Mar 03 15:23:01 2014 +0100
@@ -65,7 +65,7 @@
}
NativeBoolean(final boolean flag, final Global global) {
- this(flag, global.getBooleanPrototype(), global.getBooleanMap());
+ this(flag, global.getBooleanPrototype(), getInitialMap());
}
NativeBoolean(final boolean flag) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDate.java Mon Mar 03 15:23:01 2014 +0100
@@ -127,7 +127,7 @@
}
NativeDate(final double time, final Global global) {
- this(time, global.getDatePrototype(), global.getDateMap());
+ this(time, global.getDatePrototype(), getInitialMap());
}
private NativeDate (final double time) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java Mon Mar 03 15:23:01 2014 +0100
@@ -34,7 +34,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.PropertyListenerManager;
+import jdk.nashorn.internal.runtime.PropertyListeners;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -186,7 +186,7 @@
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static Object getListenerCount(final Object self, final Object obj) {
- return (obj instanceof ScriptObject)? ((ScriptObject)obj).getListenerCount() : 0;
+ return (obj instanceof ScriptObject) ? PropertyListeners.getListenerCount((ScriptObject) obj) : 0;
}
/**
@@ -203,14 +203,13 @@
out.println("ScriptObject count " + ScriptObject.getCount());
out.println("Scope count " + ScriptObject.getScopeCount());
- out.println("ScriptObject listeners added " + PropertyListenerManager.getListenersAdded());
- out.println("ScriptObject listeners removed " + PropertyListenerManager.getListenersRemoved());
+ out.println("ScriptObject listeners added " + PropertyListeners.getListenersAdded());
+ out.println("ScriptObject listeners removed " + PropertyListeners.getListenersRemoved());
out.println("ScriptFunction constructor calls " + ScriptFunction.getConstructorCount());
out.println("ScriptFunction invokes " + ScriptFunction.getInvokes());
out.println("ScriptFunction allocations " + ScriptFunction.getAllocations());
out.println("PropertyMap count " + PropertyMap.getCount());
out.println("PropertyMap cloned " + PropertyMap.getClonedCount());
- out.println("PropertyMap shared " + PropertyMap.getSharedCount());
out.println("PropertyMap duplicated " + PropertyMap.getDuplicatedCount());
out.println("PropertyMap history hit " + PropertyMap.getHistoryHit());
out.println("PropertyMap proto invalidations " + PropertyMap.getProtoInvalidations());
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeError.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeError.java Mon Mar 03 15:23:01 2014 +0100
@@ -108,7 +108,7 @@
}
NativeError(final Object msg, final Global global) {
- this(msg, global.getErrorPrototype(), global.getErrorMap());
+ this(msg, global.getErrorPrototype(), getInitialMap());
}
private NativeError(final Object msg) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeEvalError.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeEvalError.java Mon Mar 03 15:23:01 2014 +0100
@@ -78,7 +78,7 @@
}
NativeEvalError(final Object msg, final Global global) {
- this(msg, global.getEvalErrorPrototype(), global.getEvalErrorMap());
+ this(msg, global.getEvalErrorPrototype(), getInitialMap());
}
private NativeEvalError(final Object msg) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java Mon Mar 03 15:23:01 2014 +0100
@@ -163,7 +163,7 @@
}
private static ScriptObject wrapAdaptee(final ScriptObject adaptee) {
- return new JO(adaptee, Global.instance().getObjectMap());
+ return new JO(adaptee, JO.getInitialMap());
}
@Override
@@ -577,7 +577,7 @@
proto = global.getJSAdapterPrototype();
}
- return new NativeJSAdapter(overrides, (ScriptObject)adaptee, (ScriptObject)proto, global.getJSAdapterMap());
+ return new NativeJSAdapter(overrides, (ScriptObject)adaptee, (ScriptObject)proto, getInitialMap());
}
@Override
@@ -629,7 +629,7 @@
// to name. Probably not a big deal, but if we can ever make it leaner, it'd be nice.
return new GuardedInvocation(MH.dropArguments(MH.constant(Object.class,
func.makeBoundFunction(this, new Object[] { name })), 0, Object.class),
- adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), __call__),
+ adaptee.getProtoSwitchPoint(__call__, find.getOwner()),
testJSAdaptor(adaptee, null, null, null));
}
}
@@ -700,7 +700,7 @@
if (methodHandle != null) {
return new GuardedInvocation(
methodHandle,
- adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook),
+ adaptee.getProtoSwitchPoint(hook, findData.getOwner()),
testJSAdaptor(adaptee, findData.getGetter(Object.class), findData.getOwner(), func));
}
}
@@ -713,7 +713,7 @@
final MethodHandle methodHandle = hook.equals(__put__) ?
MH.asType(Lookup.EMPTY_SETTER, type) :
Lookup.emptyGetter(type.returnType());
- return new GuardedInvocation(methodHandle, adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook), testJSAdaptor(adaptee, null, null, null));
+ return new GuardedInvocation(methodHandle, adaptee.getProtoSwitchPoint(hook, null), testJSAdaptor(adaptee, null, null, null));
}
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJavaImporter.java Mon Mar 03 15:23:01 2014 +0100
@@ -70,7 +70,7 @@
}
private NativeJavaImporter(final Object[] args, final Global global) {
- this(args, global.getJavaImporterPrototype(), global.getJavaImporterMap());
+ this(args, global.getJavaImporterPrototype(), getInitialMap());
}
private NativeJavaImporter(final Object[] args) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java Mon Mar 03 15:23:01 2014 +0100
@@ -98,7 +98,7 @@
}
NativeNumber(final double value, final Global global) {
- this(value, global.getNumberPrototype(), global.getNumberMap());
+ this(value, global.getNumberPrototype(), getInitialMap());
}
private NativeNumber(final double value) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeRangeError.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeRangeError.java Mon Mar 03 15:23:01 2014 +0100
@@ -78,7 +78,7 @@
}
NativeRangeError(final Object msg, final Global global) {
- this(msg, global.getRangeErrorPrototype(), global.getRangeErrorMap());
+ this(msg, global.getRangeErrorPrototype(), getInitialMap());
}
private NativeRangeError(final Object msg) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeReferenceError.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeReferenceError.java Mon Mar 03 15:23:01 2014 +0100
@@ -78,7 +78,7 @@
}
NativeReferenceError(final Object msg, final Global global) {
- this(msg, global.getReferenceErrorPrototype(), global.getReferenceErrorMap());
+ this(msg, global.getReferenceErrorPrototype(), getInitialMap());
}
private NativeReferenceError(final Object msg) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java Mon Mar 03 15:23:01 2014 +0100
@@ -75,7 +75,7 @@
}
private NativeRegExp(final Global global) {
- super(global.getRegExpPrototype(), global.getRegExpMap());
+ super(global.getRegExpPrototype(), getInitialMap());
this.globalObject = global;
}
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java Mon Mar 03 15:23:01 2014 +0100
@@ -58,7 +58,7 @@
}
NativeRegExpExecResult(final RegExpResult result, final Global global) {
- super(global.getArrayPrototype(), global.getRegExpExecResultMap());
+ super(global.getArrayPrototype(), getInitialMap());
setIsArray();
this.setArray(ArrayData.allocate(result.getGroups().clone()));
this.index = result.getIndex();
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java Mon Mar 03 15:23:01 2014 +0100
@@ -60,9 +60,9 @@
// In strict mode, the caller and callee properties should throw TypeError
// Need to add properties directly to map since slots are assigned speculatively by newUserAccessors.
final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE;
- map = map.addProperty(map.newUserAccessors("caller", flags));
- map = map.addProperty(map.newUserAccessors("callee", flags));
- map$ = map.setIsShared();
+ map = map.addPropertyNoHistory(map.newUserAccessors("caller", flags));
+ map = map.addPropertyNoHistory(map.newUserAccessors("callee", flags));
+ map$ = map;
}
static PropertyMap getInitialMap() {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeString.java Mon Mar 03 15:23:01 2014 +0100
@@ -83,7 +83,7 @@
}
NativeString(final CharSequence value, final Global global) {
- this(value, global.getStringPrototype(), global.getStringMap());
+ this(value, global.getStringPrototype(), getInitialMap());
}
private NativeString(final CharSequence value, final ScriptObject proto, final PropertyMap map) {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeSyntaxError.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeSyntaxError.java Mon Mar 03 15:23:01 2014 +0100
@@ -68,7 +68,7 @@
@SuppressWarnings("LeakingThisInConstructor")
NativeSyntaxError(final Object msg, final Global global) {
- super(global.getSyntaxErrorPrototype(), global.getSyntaxErrorMap());
+ super(global.getSyntaxErrorPrototype(), getInitialMap());
if (msg != UNDEFINED) {
this.instMessage = JSType.toString(msg);
} else {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeTypeError.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeTypeError.java Mon Mar 03 15:23:01 2014 +0100
@@ -68,7 +68,7 @@
@SuppressWarnings("LeakingThisInConstructor")
NativeTypeError(final Object msg, final Global global) {
- super(global.getTypeErrorPrototype(), global.getTypeErrorMap());
+ super(global.getTypeErrorPrototype(), getInitialMap());
if (msg != UNDEFINED) {
this.instMessage = JSType.toString(msg);
} else {
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeURIError.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeURIError.java Mon Mar 03 15:23:01 2014 +0100
@@ -67,7 +67,7 @@
@SuppressWarnings("LeakingThisInConstructor")
NativeURIError(final Object msg, final Global global) {
- super(global.getURIErrorPrototype(), global.getURIErrorMap());
+ super(global.getURIErrorPrototype(), getInitialMap());
if (msg != UNDEFINED) {
this.instMessage = JSType.toString(msg);
} else {
--- a/nashorn/src/jdk/nashorn/internal/objects/PrototypeObject.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/PrototypeObject.java Mon Mar 03 15:23:01 2014 +0100
@@ -54,7 +54,7 @@
static {
final ArrayList<Property> properties = new ArrayList<>(1);
properties.add(AccessorProperty.create("constructor", Property.NOT_ENUMERABLE, GET_CONSTRUCTOR, SET_CONSTRUCTOR));
- map$ = PropertyMap.newMap(properties).setIsShared();
+ map$ = PropertyMap.newMap(properties);
}
static PropertyMap getInitialMap() {
@@ -62,8 +62,7 @@
}
private PrototypeObject(final Global global, final PropertyMap map) {
- super(map != map$? map.addAll(global.getPrototypeObjectMap()) : global.getPrototypeObjectMap());
- setProto(global.getObjectPrototype());
+ super(global.getObjectPrototype(), map != map$? map.addAll(map$) : map$);
}
PrototypeObject() {
--- a/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java Mon Mar 03 15:23:01 2014 +0100
@@ -37,7 +37,6 @@
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.runtime.AccessorProperty;
/**
@@ -76,7 +75,7 @@
private static final Object LAZY_PROTOTYPE = new Object();
private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs, final Global global) {
- super(name, invokeHandle, global.getFunctionMap(), null, specs, false, true, true);
+ super(name, invokeHandle, getInitialMap(), null, specs, false, true, true);
init(global);
}
@@ -93,7 +92,7 @@
}
private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs, final Global global) {
- super(name, invokeHandle, map.addAll(global.getFunctionMap()), null, specs, false, true, true);
+ super(name, invokeHandle, map.addAll(getInitialMap()), null, specs, false, true, true);
init(global);
}
@@ -151,7 +150,7 @@
* @param global the global object
*/
ScriptFunctionImpl(final ScriptFunctionData data, final Global global) {
- super(data, global.getBoundFunctionMap(), null);
+ super(data, getInitialBoundMap(), null);
init(global);
}
@@ -163,25 +162,20 @@
map$ = PropertyMap.newMap(properties);
strictmodemap$ = createStrictModeMap(map$);
boundfunctionmap$ = createBoundFunctionMap(strictmodemap$);
- // There are order dependencies between normal map, struct map and bound map
- // We can make these 'shared' only after initialization of all three.
- map$.setIsShared();
- strictmodemap$.setIsShared();
- boundfunctionmap$.setIsShared();
}
private static PropertyMap createStrictModeMap(final PropertyMap map) {
final int flags = Property.NOT_ENUMERABLE | Property.NOT_CONFIGURABLE;
PropertyMap newMap = map;
// Need to add properties directly to map since slots are assigned speculatively by newUserAccessors.
- newMap = newMap.addProperty(map.newUserAccessors("arguments", flags));
- newMap = newMap.addProperty(map.newUserAccessors("caller", flags));
+ newMap = newMap.addPropertyNoHistory(map.newUserAccessors("arguments", flags));
+ newMap = newMap.addPropertyNoHistory(map.newUserAccessors("caller", flags));
return newMap;
}
// Choose the map based on strict mode!
private static PropertyMap getMap(final Global global, final boolean strict) {
- return strict ? global.getStrictFunctionMap() : global.getFunctionMap();
+ return strict ? getInitialStrictMap() : getInitialMap();
}
private static PropertyMap createBoundFunctionMap(final PropertyMap strictModeMap) {
@@ -193,14 +187,14 @@
// Instance of this class is used as global anonymous function which
// serves as Function.prototype object.
private static class AnonymousFunction extends ScriptFunctionImpl {
- private static final PropertyMap anonmap$ = PropertyMap.newMap().setIsShared();
+ private static final PropertyMap anonmap$ = PropertyMap.newMap();
static PropertyMap getInitialMap() {
return anonmap$;
}
AnonymousFunction(final Global global) {
- super("", GlobalFunctions.ANONYMOUS, global.getAnonymousFunctionMap(), null);
+ super("", GlobalFunctions.ANONYMOUS, getInitialAnonymousMap(), null);
}
}
@@ -281,13 +275,17 @@
}
@Override
- public final void setPrototype(final Object prototype) {
- this.prototype = prototype;
+ public final void setPrototype(final Object newProto) {
+ if (newProto instanceof ScriptObject && newProto != this.prototype && allocatorMap != null) {
+ // Replace our current allocator map with one that is associated with the new prototype.
+ allocatorMap = allocatorMap.changeProto((ScriptObject)newProto);
+ }
+ this.prototype = newProto;
}
// Internals below..
private void init(final Global global) {
- this.setProto(global.getFunctionPrototype());
+ this.setInitialProto(global.getFunctionPrototype());
this.prototype = LAZY_PROTOTYPE;
// We have to fill user accessor functions late as these are stored
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyListener.java Tue Feb 25 18:56:10 2014 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, 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;
-
-/**
- * Property change listener gets notified whenever properties are added/deleted/modified.
- */
-public interface PropertyListener {
- /**
- * A new property is being added.
- *
- * @param object The ScriptObject to which property was added.
- * @param prop The new Property added.
- */
- public void propertyAdded(ScriptObject object, Property prop);
-
- /**
- * An existing property is being deleted.
- *
- * @param object The ScriptObject whose property is being deleted.
- * @param prop The property being deleted.
- */
- public void propertyDeleted(ScriptObject object, Property prop);
-
- /**
- * An existing Property is being replaced with a new Property.
- *
- * @param object The ScriptObject whose property is being modified.
- * @param oldProp The old property that is being replaced.
- * @param newProp The new property that replaces the old property.
- *
- */
- public void propertyModified(ScriptObject object, Property oldProp, Property newProp);
-
- /**
- * Given object's __proto__ has changed.
- *
- * @param object object whose __proto__ has changed.
- * @param oldProto old __proto__
- * @param newProto new __proto__
- */
- public void protoChanged(ScriptObject object, ScriptObject oldProto, ScriptObject newProto);
-}
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java Tue Feb 25 18:56:10 2014 +0530
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, 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.WeakHashMap;
-
-/**
- * Helper class to manage property listeners and notification.
- */
-public class PropertyListenerManager implements PropertyListener {
- PropertyListenerManager() {}
-
- /** property listeners for this object. */
- private Map<PropertyListener,Boolean> listeners;
-
- // These counters are updated in debug mode
- private static int listenersAdded;
- private static int listenersRemoved;
-
- /**
- * Return aggregate listeners added to all PropertyListenerManagers
- * @return the listenersAdded
- */
- public static int getListenersAdded() {
- return listenersAdded;
- }
-
- /**
- * Return aggregate listeners removed from all PropertyListenerManagers
- * @return the listenersRemoved
- */
- public static int getListenersRemoved() {
- return listenersRemoved;
- }
-
- /**
- * Return listeners added to this PropertyListenerManager.
- * @return the listener count
- */
- public final int getListenerCount() {
- return listeners != null? listeners.size() : 0;
- }
-
- // Property listener management methods
-
- /**
- * Add a property listener to this object.
- *
- * @param listener The property listener that is added.
- */
- public synchronized final void addPropertyListener(final PropertyListener listener) {
- if (listeners == null) {
- listeners = new WeakHashMap<>();
- }
-
- if (Context.DEBUG) {
- listenersAdded++;
- }
- listeners.put(listener, Boolean.TRUE);
- }
-
- /**
- * Remove a property listener from this object.
- *
- * @param listener The property listener that is removed.
- */
- public synchronized final void removePropertyListener(final PropertyListener listener) {
- if (listeners != null) {
- if (Context.DEBUG) {
- listenersRemoved++;
- }
- listeners.remove(listener);
- }
- }
-
- /**
- * This method can be called to notify property addition to this object's listeners.
- *
- * @param object The ScriptObject to which property was added.
- * @param prop The property being added.
- */
- protected synchronized final void notifyPropertyAdded(final ScriptObject object, final Property prop) {
- if (listeners != null) {
- for (PropertyListener listener : listeners.keySet()) {
- listener.propertyAdded(object, prop);
- }
- }
- }
-
- /**
- * This method can be called to notify property deletion to this object's listeners.
- *
- * @param object The ScriptObject from which property was deleted.
- * @param prop The property being deleted.
- */
- protected synchronized final void notifyPropertyDeleted(final ScriptObject object, final Property prop) {
- if (listeners != null) {
- for (PropertyListener listener : listeners.keySet()) {
- listener.propertyDeleted(object, prop);
- }
- }
- }
-
- /**
- * This method can be called to notify property modification to this object's listeners.
- *
- * @param object The ScriptObject to which property was modified.
- * @param oldProp The old property being replaced.
- * @param newProp The new property that replaces the old property.
- */
- protected synchronized final void notifyPropertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
- if (listeners != null) {
- for (PropertyListener listener : listeners.keySet()) {
- listener.propertyModified(object, oldProp, newProp);
- }
- }
- }
-
- /**
- * This method can be called to notify __proto__ modification to this object's listeners.
- *
- * @param object The ScriptObject whose __proto__ was changed.
- * @param oldProto old __proto__
- * @param newProto new __proto__
- */
- protected synchronized final void notifyProtoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
- if (listeners != null) {
- for (PropertyListener listener : listeners.keySet()) {
- listener.protoChanged(object, oldProto, newProto);
- }
- }
- }
-
- // PropertyListener methods
-
- @Override
- public final void propertyAdded(final ScriptObject object, final Property prop) {
- notifyPropertyAdded(object, prop);
- }
-
- @Override
- public final void propertyDeleted(final ScriptObject object, final Property prop) {
- notifyPropertyDeleted(object, prop);
- }
-
- @Override
- public final void propertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
- notifyPropertyModified(object, oldProp, newProp);
- }
-
- @Override
- public final void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
- notifyProtoChanged(object, oldProto, newProto);
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyListeners.java Mon Mar 03 15:23:01 2014 +0100
@@ -0,0 +1,222 @@
+/*
+ * 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;
+
+/**
+ * Helper class to manage property listeners and notification.
+ */
+public class PropertyListeners {
+
+ private Map<String, WeakPropertyMapSet> listeners;
+
+ // These counters are updated in debug mode
+ private static int listenersAdded;
+ private static int listenersRemoved;
+
+ /**
+ * Copy constructor
+ * @param listener listener to copy
+ */
+ PropertyListeners(final PropertyListeners listener) {
+ if (listener != null && listener.listeners != null) {
+ this.listeners = new WeakHashMap<>(listener.listeners);
+ }
+ }
+
+ /**
+ * Return aggregate listeners added to all PropertyListenerManagers
+ * @return the listenersAdded
+ */
+ public static int getListenersAdded() {
+ return listenersAdded;
+ }
+
+ /**
+ * Return aggregate listeners removed from all PropertyListenerManagers
+ * @return the listenersRemoved
+ */
+ public static int getListenersRemoved() {
+ return listenersRemoved;
+ }
+
+ /**
+ * Return listeners added to this ScriptObject.
+ * @param obj the object
+ * @return the listener count
+ */
+ public static int getListenerCount(final ScriptObject obj) {
+ final PropertyListeners propertyListeners = obj.getMap().getListeners();
+ if (propertyListeners != null) {
+ return propertyListeners.listeners == null ? 0 : propertyListeners.listeners.size();
+ }
+ return 0;
+ }
+
+ // 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;
+ }
+ 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++;
+ }
+ 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) {
+ WeakPropertyMapSet set = listeners.get(prop.getKey());
+ if (set != null) {
+ for (PropertyMap propertyMap : set.elements()) {
+ propertyMap.propertyAdded(prop);
+ }
+ listeners.remove(prop.getKey());
+ }
+ }
+ }
+
+ /**
+ * An existing property is being deleted.
+ *
+ * @param prop The property being deleted.
+ */
+ public synchronized void propertyDeleted(final Property prop) {
+ if (listeners != null) {
+ WeakPropertyMapSet set = listeners.get(prop.getKey());
+ if (set != null) {
+ for (PropertyMap propertyMap : set.elements()) {
+ propertyMap.propertyDeleted(prop);
+ }
+ listeners.remove(prop.getKey());
+ }
+ }
+ }
+
+ /**
+ * 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) {
+ WeakPropertyMapSet set = listeners.get(oldProp.getKey());
+ if (set != null) {
+ for (PropertyMap propertyMap : set.elements()) {
+ propertyMap.propertyModified(oldProp, newProp);
+ }
+ listeners.remove(oldProp.getKey());
+ }
+ }
+ }
+
+ public synchronized void protoChanged() {
+ if (listeners != null) {
+ for (WeakPropertyMapSet set : listeners.values()) {
+ for (PropertyMap propertyMap : set.elements()) {
+ propertyMap.protoChanged();
+ }
+ }
+ listeners.clear();
+ }
+ }
+
+ private static class WeakPropertyMapSet {
+
+ private WeakHashMap<PropertyMap, Boolean> map = new WeakHashMap<>();
+
+ 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();
+ }
+
+ }
+}
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java Mon Mar 03 15:23:01 2014 +0100
@@ -30,13 +30,11 @@
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import java.lang.invoke.SwitchPoint;
-import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
import java.util.NoSuchElementException;
import java.util.WeakHashMap;
@@ -49,17 +47,11 @@
* All property maps are immutable. If a property is added, modified or removed, the mutator
* will return a new map.
*/
-public final class PropertyMap implements Iterable<Object>, PropertyListener {
+public final class PropertyMap implements Iterable<Object> {
/** Used for non extensible PropertyMaps, negative logic as the normal case is extensible. See {@link ScriptObject#preventExtensions()} */
public static final int NOT_EXTENSIBLE = 0b0000_0001;
/** Does this map contain valid array keys? */
public static final int CONTAINS_ARRAY_KEYS = 0b0000_0010;
- /** This mask is used to preserve certain flags when cloning the PropertyMap. Others should not be copied */
- private static final int CLONEABLE_FLAGS_MASK = 0b0000_1111;
- /** Has a listener been added to this property map. This flag is not copied when cloning a map. See {@link PropertyListener} */
- public static final int IS_LISTENER_ADDED = 0b0001_0000;
- /** Is this process wide "shared" map?. This flag is not copied when cloning a map */
- public static final int IS_SHARED = 0b0010_0000;
/** Map status flags. */
private int flags;
@@ -77,16 +69,16 @@
private int spillLength;
/** {@link SwitchPoint}s for gets on inherited properties. */
- private Map<String, SwitchPoint> protoGetSwitches;
+ private HashMap<String, SwitchPoint> protoGetSwitches;
/** History of maps, used to limit map duplication. */
- private HashMap<Property, PropertyMap> history;
+ private WeakHashMap<Property, SoftReference<PropertyMap>> history;
/** History of prototypes, used to limit map duplication. */
- private WeakHashMap<ScriptObject, WeakReference<PropertyMap>> protoHistory;
+ private WeakHashMap<PropertyMap, SoftReference<PropertyMap>> protoHistory;
- /** Cache for hashCode */
- private int hashCode;
+ /** property listeners */
+ private PropertyListeners listeners;
/**
* Constructor.
@@ -119,10 +111,12 @@
*/
private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties) {
this.properties = properties;
- this.flags = propertyMap.getClonedFlags();
+ this.flags = propertyMap.flags;
this.spillLength = propertyMap.spillLength;
this.fieldCount = propertyMap.fieldCount;
this.fieldMaximum = propertyMap.fieldMaximum;
+ // We inherit the parent property listeners instance. It will be cloned when a new listener is added.
+ this.listeners = propertyMap.listeners;
if (Context.DEBUG) {
count++;
@@ -203,35 +197,92 @@
}
/**
+ * Get the listeners of this map, or null if none exists
+ *
+ * @return the listeners
+ */
+ public PropertyListeners getListeners() {
+ return listeners;
+ }
+
+ /**
+ * Add {@code listenerMap} as a listener to this property map for the given {@code key}.
+ *
+ * @param key the property name
+ * @param listenerMap the listener map
+ */
+ 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);
+ }
+ }
+
+ /**
+ * A new property is being added.
+ *
+ * @param property The new Property added.
+ */
+ public void propertyAdded(final Property property) {
+ invalidateProtoGetSwitchPoint(property);
+ if (listeners != null) {
+ listeners.propertyAdded(property);
+ }
+ }
+
+ /**
+ * An existing property is being deleted.
+ *
+ * @param property The property being deleted.
+ */
+ public void propertyDeleted(final Property property) {
+ invalidateProtoGetSwitchPoint(property);
+ if (listeners != null) {
+ listeners.propertyDeleted(property);
+ }
+ }
+
+ /**
+ * An existing property is being redefined.
+ *
+ * @param oldProperty The old property
+ * @param newProperty The new property
+ */
+ public void propertyModified(final Property oldProperty, final Property newProperty) {
+ invalidateProtoGetSwitchPoint(oldProperty);
+ if (listeners != null) {
+ listeners.propertyModified(oldProperty, newProperty);
+ }
+ }
+
+ /**
+ * The prototype of an object associated with this {@link PropertyMap} is changed.
+ */
+ public void protoChanged() {
+ invalidateAllProtoGetSwitchPoints();
+ if (listeners != null) {
+ listeners.protoChanged();
+ }
+ }
+
+ /**
* Return a SwitchPoint used to track changes of a property in a prototype.
*
- * @param proto Object prototype.
- * @param key {@link Property} key.
- *
+ * @param key Property key.
* @return A shared {@link SwitchPoint} for the property.
*/
- public SwitchPoint getProtoGetSwitchPoint(final ScriptObject proto, final String key) {
- assert !isShared() : "proto SwitchPoint from a shared PropertyMap";
-
- if (proto == null) {
- return null;
+ public synchronized SwitchPoint getSwitchPoint(final String key) {
+ if (protoGetSwitches == null) {
+ protoGetSwitches = new HashMap<>();
}
- if (protoGetSwitches == null) {
- protoGetSwitches = new HashMap<>();
- if (! isListenerAdded()) {
- proto.addPropertyListener(this);
- setIsListenerAdded();
- }
+ SwitchPoint switchPoint = protoGetSwitches.get(key);
+ if (switchPoint == null) {
+ switchPoint = new SwitchPoint();
+ protoGetSwitches.put(key, switchPoint);
}
- if (protoGetSwitches.containsKey(key)) {
- return protoGetSwitches.get(key);
- }
-
- final SwitchPoint switchPoint = new SwitchPoint();
- protoGetSwitches.put(key, switchPoint);
-
return switchPoint;
}
@@ -240,14 +291,13 @@
*
* @param property {@link Property} to invalidate.
*/
- private void invalidateProtoGetSwitchPoint(final Property property) {
- assert !isShared() : "proto invalidation on a shared PropertyMap";
+ synchronized void invalidateProtoGetSwitchPoint(final Property property) {
+ if (protoGetSwitches != null) {
- if (protoGetSwitches != null) {
final String key = property.getKey();
final SwitchPoint sp = protoGetSwitches.get(key);
if (sp != null) {
- protoGetSwitches.put(key, new SwitchPoint());
+ protoGetSwitches.remove(key);
if (Context.DEBUG) {
protoInvalidations++;
}
@@ -257,14 +307,15 @@
}
/**
- * Indicate that proto itself has changed in hierachy somewhere.
+ * Indicate that proto itself has changed in hierarchy somewhere.
*/
- private void invalidateAllProtoGetSwitchPoints() {
- assert !isShared() : "proto invalidation on a shared PropertyMap";
-
- if (protoGetSwitches != null) {
- final Collection<SwitchPoint> sws = protoGetSwitches.values();
- SwitchPoint.invalidateAll(sws.toArray(new SwitchPoint[sws.size()]));
+ synchronized void invalidateAllProtoGetSwitchPoints() {
+ if (protoGetSwitches != null && !protoGetSwitches.isEmpty()) {
+ if (Context.DEBUG) {
+ protoInvalidations += protoGetSwitches.size();
+ }
+ SwitchPoint.invalidateAll(protoGetSwitches.values().toArray(new SwitchPoint[protoGetSwitches.values().size()]));
+ protoGetSwitches.clear();
}
}
@@ -279,7 +330,33 @@
* @return New {@link PropertyMap} with {@link Property} added.
*/
PropertyMap addPropertyBind(final AccessorProperty property, final Object bindTo) {
- return addProperty(new AccessorProperty(property, bindTo));
+ // No need to store bound property in the history as bound properties can't be reused.
+ return addPropertyNoHistory(new AccessorProperty(property, bindTo));
+ }
+
+ /**
+ * Add a property to the map without adding it to the history. This should be used for properties that
+ * can't be shared such as bound properties, or properties that are expected to be added only once.
+ *
+ * @param property {@link Property} being added.
+ * @return New {@link PropertyMap} with {@link Property} added.
+ */
+ public PropertyMap addPropertyNoHistory(final Property property) {
+ if (listeners != null) {
+ listeners.propertyAdded(property);
+ }
+ final PropertyHashMap newProperties = properties.immutableAdd(property);
+ final PropertyMap newMap = new PropertyMap(this, newProperties);
+
+ if(!property.isSpill()) {
+ newMap.fieldCount = Math.max(newMap.fieldCount, property.getSlot() + 1);
+ }
+ if (isValidArrayIndex(getArrayIndex(property.getKey()))) {
+ newMap.setContainsArrayKeys();
+ }
+
+ newMap.spillLength += property.getSpillCount();
+ return newMap;
}
/**
@@ -290,6 +367,9 @@
* @return New {@link PropertyMap} with {@link Property} added.
*/
public PropertyMap addProperty(final Property property) {
+ if (listeners != null) {
+ listeners.propertyAdded(property);
+ }
PropertyMap newMap = checkHistory(property);
if (newMap == null) {
@@ -318,6 +398,9 @@
* @return New {@link PropertyMap} with {@link Property} removed or {@code null} if not found.
*/
public PropertyMap deleteProperty(final Property property) {
+ if (listeners != null) {
+ listeners.propertyDeleted(property);
+ }
PropertyMap newMap = checkHistory(property);
final String key = property.getKey();
@@ -339,6 +422,9 @@
* @return New {@link PropertyMap} with {@link Property} replaced.
*/
PropertyMap replaceProperty(final Property oldProperty, final Property newProperty) {
+ if (listeners != null) {
+ listeners.propertyModified(oldProperty, newProperty);
+ }
// Add replaces existing property.
final PropertyHashMap newProperties = properties.immutableAdd(newProperty);
final PropertyMap newMap = new PropertyMap(this, newProperties);
@@ -363,7 +449,7 @@
(oldProperty instanceof AccessorProperty &&
newProperty instanceof UserAccessorProperty) : "arbitrary replaceProperty attempted";
- newMap.flags = getClonedFlags();
+ newMap.flags = flags;
/*
* spillLength remains same in case (1) and (2) because of slot reuse. Only for case (3), we need
@@ -491,28 +577,6 @@
}
/**
- * Make this property map 'shared' one. Shared property map instances are
- * process wide singleton objects. A shaped map should never be added as a listener
- * to a proto object. Nor it should have history or proto history. A shared map
- * is just a template that is meant to be duplicated before use. All nasgen initialized
- * property maps are shared.
- *
- * @return this map after making it as shared
- */
- public PropertyMap setIsShared() {
- assert !isListenerAdded() : "making PropertyMap shared after listener added";
- assert protoHistory == null : "making PropertyMap shared after associating a proto with it";
- if (Context.DEBUG) {
- sharedCount++;
- }
-
- flags |= IS_SHARED;
- // clear any history on this PropertyMap, won't be used.
- history = null;
- return this;
- }
-
- /**
* Check for any configurable properties.
*
* @return {@code true} if any configurable.
@@ -551,14 +615,14 @@
/**
* Check prototype history for an existing property map with specified prototype.
*
- * @param newProto New prototype object.
+ * @param parentMap New prototype object.
*
* @return Existing {@link PropertyMap} or {@code null} if not found.
*/
- private PropertyMap checkProtoHistory(final ScriptObject newProto) {
+ private PropertyMap checkProtoHistory(final PropertyMap parentMap) {
final PropertyMap cachedMap;
if (protoHistory != null) {
- final WeakReference<PropertyMap> weakMap = protoHistory.get(newProto);
+ final SoftReference<PropertyMap> weakMap = protoHistory.get(parentMap);
cachedMap = (weakMap != null ? weakMap.get() : null);
} else {
cachedMap = null;
@@ -574,17 +638,15 @@
/**
* Add a map to the prototype history.
*
- * @param newProto Prototype to add (key.)
+ * @param parentMap Prototype to add (key.)
* @param newMap {@link PropertyMap} associated with prototype.
*/
- private void addToProtoHistory(final ScriptObject newProto, final PropertyMap newMap) {
- assert !isShared() : "proto history modified on a shared PropertyMap";
-
+ private void addToProtoHistory(final PropertyMap parentMap, final PropertyMap newMap) {
if (protoHistory == null) {
protoHistory = new WeakHashMap<>();
}
- protoHistory.put(newProto, new WeakReference<>(newMap));
+ protoHistory.put(parentMap, new SoftReference<>(newMap));
}
/**
@@ -594,14 +656,12 @@
* @param newMap Modified {@link PropertyMap}.
*/
private void addToHistory(final Property property, final PropertyMap newMap) {
- assert !isShared() : "history modified on a shared PropertyMap";
-
if (!properties.isEmpty()) {
if (history == null) {
- history = new LinkedHashMap<>();
+ history = new WeakHashMap<>();
}
- history.put(property, newMap);
+ history.put(property, new SoftReference<>(newMap));
}
}
@@ -613,8 +673,10 @@
* @return Existing map or {@code null} if not found.
*/
private PropertyMap checkHistory(final Property property) {
+
if (history != null) {
- PropertyMap historicMap = history.get(property);
+ SoftReference<PropertyMap> ref = history.get(property);
+ final PropertyMap historicMap = ref == null ? null : ref.get();
if (historicMap != null) {
if (Context.DEBUG) {
@@ -628,54 +690,6 @@
return null;
}
- /**
- * Calculate the hash code for the map.
- *
- * @return Computed hash code.
- */
- private int computeHashCode() {
- int hash = 0;
-
- for (final Property property : getProperties()) {
- hash = hash << 7 ^ hash >> 7;
- hash ^= property.hashCode();
- }
-
- return hash;
- }
-
- @Override
- public int hashCode() {
- if (hashCode == 0 && !properties.isEmpty()) {
- hashCode = computeHashCode();
- }
- return hashCode;
- }
-
- @Override
- public boolean equals(final Object other) {
- if (!(other instanceof PropertyMap)) {
- return false;
- }
-
- final PropertyMap otherMap = (PropertyMap)other;
-
- if (properties.size() != otherMap.properties.size()) {
- return false;
- }
-
- final Iterator<Property> iter = properties.values().iterator();
- final Iterator<Property> otherIter = otherMap.properties.values().iterator();
-
- while (iter.hasNext() && otherIter.hasNext()) {
- if (!iter.next().equals(otherIter.next())) {
- return false;
- }
- }
-
- return true;
- }
-
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
@@ -728,24 +742,6 @@
}
/**
- * Check whether a {@link PropertyListener} has been added to this map.
- *
- * @return {@code true} if {@link PropertyListener} exists
- */
- public boolean isListenerAdded() {
- return (flags & IS_LISTENER_ADDED) != 0;
- }
-
- /**
- * Check if this map shared or not.
- *
- * @return true if this map is shared.
- */
- public boolean isShared() {
- return (flags & IS_SHARED) != 0;
- }
-
- /**
* Test to see if {@link PropertyMap} is extensible.
*
* @return {@code true} if {@link PropertyMap} can be added to.
@@ -800,50 +796,29 @@
}
/**
- * Change the prototype of objects associated with this {@link PropertyMap}.
+ * Return a property map with the same layout that is associated with the new prototype object.
*
- * @param oldProto Current prototype object.
* @param newProto New prototype object to replace oldProto.
- *
* @return New {@link PropertyMap} with prototype changed.
*/
- PropertyMap changeProto(final ScriptObject oldProto, final ScriptObject newProto) {
- assert !isShared() : "proto associated with a shared PropertyMap";
+ public PropertyMap changeProto(final ScriptObject newProto) {
- if (oldProto == newProto) {
- return this;
- }
-
- final PropertyMap nextMap = checkProtoHistory(newProto);
+ final PropertyMap parentMap = newProto == null ? null : newProto.getMap();
+ final PropertyMap nextMap = checkProtoHistory(parentMap);
if (nextMap != null) {
return nextMap;
}
if (Context.DEBUG) {
- incrementSetProtoNewMapCount();
+ setProtoNewMapCount++;
}
final PropertyMap newMap = new PropertyMap(this);
- addToProtoHistory(newProto, newMap);
+ addToProtoHistory(parentMap, newMap);
return newMap;
}
- /**
- * Indicate that the map has listeners.
- */
- private void setIsListenerAdded() {
- flags |= IS_LISTENER_ADDED;
- }
-
- /**
- * Return only the flags that should be copied during cloning.
- *
- * @return Subset of flags that should be copied.
- */
- private int getClonedFlags() {
- return flags & CLONEABLE_FLAGS_MASK;
- }
/**
* {@link PropertyMap} iterator.
@@ -900,41 +875,12 @@
}
/*
- * PropertyListener implementation.
- */
-
- @Override
- public void propertyAdded(final ScriptObject object, final Property prop) {
- invalidateProtoGetSwitchPoint(prop);
- }
-
- @Override
- public void propertyDeleted(final ScriptObject object, final Property prop) {
- invalidateProtoGetSwitchPoint(prop);
- }
-
- @Override
- public void propertyModified(final ScriptObject object, final Property oldProp, final Property newProp) {
- invalidateProtoGetSwitchPoint(oldProp);
- }
-
- @Override
- public void protoChanged(final ScriptObject object, final ScriptObject oldProto, final ScriptObject newProto) {
- // We may walk and invalidate SwitchPoints for properties inherited
- // from 'object' or it's old proto chain. But, it may not be worth it.
- // For example, a new proto may have a user defined getter/setter for
- // a data property down the chain. So, invalidating all is better.
- invalidateAllProtoGetSwitchPoints();
- }
-
- /*
* Debugging and statistics.
*/
// counters updated only in debug mode
private static int count;
private static int clonedCount;
- private static int sharedCount;
private static int duplicatedCount;
private static int historyHit;
private static int protoInvalidations;
@@ -956,13 +902,6 @@
}
/**
- * @return The number of maps that are shared.
- */
- public static int getSharedCount() {
- return sharedCount;
- }
-
- /**
* @return The number of maps that are duplicated.
*/
public static int getDuplicatedCount() {
@@ -997,10 +936,4 @@
return setProtoNewMapCount;
}
- /**
- * Increment the prototype set count.
- */
- private static void incrementSetProtoNewMapCount() {
- setProtoNewMapCount++;
- }
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java Mon Mar 03 15:23:01 2014 +0100
@@ -160,10 +160,10 @@
}
@Override
- ScriptObject allocate() {
+ ScriptObject allocate(final PropertyMap map) {
try {
ensureHasAllocator(); //if allocatorClass name is set to null (e.g. for bound functions) we don't even try
- return allocator == null ? null : (ScriptObject)allocator.invokeExact(allocatorMap);
+ return allocator == null ? null : (ScriptObject)allocator.invokeExact(map);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
@@ -178,6 +178,11 @@
}
@Override
+ PropertyMap getAllocatorMap() {
+ return allocatorMap;
+ }
+
+ @Override
protected synchronized void ensureCodeGenerated() {
if (!code.isEmpty()) {
return; // nothing to do, we have code, at least some.
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java Mon Mar 03 15:23:01 2014 +0100
@@ -80,6 +80,9 @@
private final ScriptFunctionData data;
+ /** The property map used for newly allocated object when function is used as constructor. */
+ protected PropertyMap allocatorMap;
+
/**
* Constructor
*
@@ -125,6 +128,7 @@
this.data = data;
this.scope = scope;
+ this.allocatorMap = data.getAllocatorMap();
}
@Override
@@ -229,16 +233,16 @@
}
assert !isBoundFunction(); // allocate never invoked on bound functions
- final ScriptObject object = data.allocate();
+ final ScriptObject object = data.allocate(allocatorMap);
if (object != null) {
Object prototype = getPrototype();
if (prototype instanceof ScriptObject) {
- object.setProto((ScriptObject)prototype);
+ object.setInitialProto((ScriptObject)prototype);
}
if (object.getProto() == null) {
- object.setProto(getObjectPrototype());
+ object.setInitialProto(getObjectPrototype());
}
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java Mon Mar 03 15:23:01 2014 +0100
@@ -229,9 +229,20 @@
/**
* Allocates an object using this function's allocator.
+ *
+ * @param map the property map for the allocated object.
* @return the object allocated using this function's allocator, or null if the function doesn't have an allocator.
*/
- ScriptObject allocate() {
+ ScriptObject allocate(final PropertyMap map) {
+ return null;
+ }
+
+ /**
+ * Get the property map to use for objects allocated by this function.
+ *
+ * @return the property map for allocated objects.
+ */
+ PropertyMap getAllocatorMap() {
return null;
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java Mon Mar 03 15:23:01 2014 +0100
@@ -43,6 +43,7 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
@@ -88,7 +89,7 @@
* </ul>
*/
-public abstract class ScriptObject extends PropertyListenerManager implements PropertyAccess {
+public abstract class ScriptObject implements PropertyAccess {
/** __proto__ special property name */
public static final String PROTO_PROPERTY_NAME = "__proto__";
@@ -107,9 +108,6 @@
/** Per ScriptObject flag - is this an arguments object? */
public static final int IS_ARGUMENTS = 0b0000_0100;
- /** Is this a prototype PropertyMap? */
- public static final int IS_PROTOTYPE = 0b0000_1000;
-
/** Is length property not-writable? */
public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
@@ -155,7 +153,7 @@
public static final Call GET_PROTO = virtualCallNoLookup(ScriptObject.class, "getProto", ScriptObject.class);
/** Method handle for setting the proto of a ScriptObject */
- public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setProto", void.class, ScriptObject.class);
+ public static final Call SET_PROTO = virtualCallNoLookup(ScriptObject.class, "setInitialProto", void.class, ScriptObject.class);
/** Method handle for setting the proto of a ScriptObject after checking argument */
public static final Call SET_PROTO_CHECK = virtualCallNoLookup(ScriptObject.class, "setProtoCheck", void.class, Object.class);
@@ -201,10 +199,6 @@
this.arrayData = ArrayData.EMPTY_ARRAY;
this.setMap(map == null ? PropertyMap.newMap() : map);
this.proto = proto;
-
- if (proto != null) {
- proto.setIsPrototype();
- }
}
/**
@@ -232,7 +226,7 @@
if (oldProp == null) {
if (property instanceof UserAccessorProperty) {
final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
- newMap = newMap.addProperty(prop);
+ newMap = newMap.addPropertyNoHistory(prop);
} else {
newMap = newMap.addPropertyBind((AccessorProperty)property, source);
}
@@ -875,8 +869,6 @@
newProperty = newUserAccessors(oldProperty.getKey(), propertyFlags, getter, setter);
}
- notifyPropertyModified(this, oldProperty, newProperty);
-
return modifyOwnProperty(oldProperty, newProperty);
}
@@ -1120,26 +1112,30 @@
*/
public synchronized final void setProto(final ScriptObject newProto) {
final ScriptObject oldProto = proto;
- map = map.changeProto(oldProto, newProto);
-
- if (newProto != null) {
- newProto.setIsPrototype();
+
+ if (oldProto != newProto) {
+ proto = newProto;
+
+ // Let current listeners know that the protototype has changed and set our map
+ final PropertyListeners listeners = getMap().getListeners();
+ if (listeners != null) {
+ listeners.protoChanged();
+ }
+ // Replace our current allocator map with one that is associated with the new prototype.
+ setMap(getMap().changeProto(newProto));
}
-
- proto = newProto;
-
- if (isPrototype()) {
- // tell listeners that my __proto__ has been changed
- notifyProtoChanged(this, oldProto, newProto);
-
- if (oldProto != null) {
- oldProto.removePropertyListener(this);
- }
-
- if (newProto != null) {
- newProto.addPropertyListener(this);
- }
- }
+ }
+
+ /**
+ * Set the initial __proto__ of this object. This should be used instead of
+ * {@link #setProto} if it is known that the current property map will not be
+ * used on a new object with any other parent property map, so we can pass over
+ * property map invalidation/evolution.
+ *
+ * @param initialProto the initial __proto__ to set.
+ */
+ public void setInitialProto(final ScriptObject initialProto) {
+ this.proto = initialProto;
}
/**
@@ -1332,25 +1328,6 @@
}
/**
- * Check if this object is a prototype
- *
- * @return {@code true} if is prototype
- */
- public final boolean isPrototype() {
- return (flags & IS_PROTOTYPE) != 0;
- }
-
- /**
- * Flag this object as having a prototype.
- */
- public final void setIsPrototype() {
- if (proto != null && !isPrototype()) {
- proto.addPropertyListener(this);
- }
- flags |= IS_PROTOTYPE;
- }
-
- /**
* Check if this object has non-writable length property
*
* @return {@code true} if 'length' property is non-writable
@@ -1791,6 +1768,7 @@
final boolean noGuard = ObjectClassGenerator.OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType();
// getMap() is fine as we have the prototype switchpoint depending on where the property was found
final MethodHandle guard = noGuard ? null : NashornGuards.getMapGuard(getMap());
+ final ScriptObject owner = find.getOwner();
if (methodHandle != null) {
assert methodHandle.type().returnType().equals(returnType);
@@ -1798,18 +1776,18 @@
return new GuardedInvocation(methodHandle, guard);
}
- if (!property.hasGetterFunction(find.getOwner())) {
+ if (!property.hasGetterFunction(owner)) {
// If not a scope bind to actual prototype as changing prototype will change the property map.
// For scopes we install a filter that replaces the self object with the prototype owning the property.
methodHandle = isScope() ?
addProtoFilter(methodHandle, find.getProtoChainLength()) :
- bindTo(methodHandle, find.getOwner());
+ bindTo(methodHandle, owner);
}
- return new GuardedInvocation(methodHandle, noGuard ? null : getMap().getProtoGetSwitchPoint(proto, name), guard);
+ return new GuardedInvocation(methodHandle, noGuard ? null : getProtoSwitchPoint(name, owner), guard);
}
assert !NashornCallSiteDescriptor.isFastScope(desc);
- return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
+ return new GuardedInvocation(Lookup.emptyGetter(returnType), getProtoSwitchPoint(name, owner), guard);
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
@@ -1866,6 +1844,28 @@
}
/**
+ * 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.
+ *
+ * @param name the property name
+ * @param owner the property owner, null if property is not defined
+ * @return a SwitchPoint or null
+ */
+ public final SwitchPoint getProtoSwitchPoint(final String name, final ScriptObject owner) {
+ if (owner == this || getProto() == null) {
+ return null;
+ }
+
+ for (ScriptObject obj = this; obj != owner && obj.getProto() != null; obj = obj.getProto()) {
+ ScriptObject parent = obj.getProto();
+ parent.getMap().addListener(name, obj.getMap());
+ }
+
+ return getMap().getSwitchPoint(name);
+ }
+
+ /**
* Find the appropriate SET method for an invoke dynamic call.
*
* @param desc the call site descriptor
@@ -1921,8 +1921,7 @@
throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
}
assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
- final PropertyMap myMap = getMap();
- return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
+ return new GuardedInvocation(Lookup.EMPTY_SETTER, getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap()));
}
@SuppressWarnings("unused")
@@ -2082,7 +2081,7 @@
methodHandle = bindTo(methodHandle, UNDEFINED);
}
return new GuardedInvocation(methodHandle,
- find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
+ getProtoSwitchPoint(NO_SUCH_PROPERTY_NAME, find.getOwner()),
getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
}
}
@@ -2134,7 +2133,8 @@
}
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
- return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
+ return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()),
+ getProtoSwitchPoint(name, null), NashornGuards.getMapGuard(getMap()));
}
private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
@@ -2215,12 +2215,10 @@
if (fieldCount < fieldMaximum) {
property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
- notifyPropertyAdded(this, property);
property = addOwnProperty(property);
} else {
int i = getMap().getSpillLength();
property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i);
- notifyPropertyAdded(this, property);
property = addOwnProperty(property);
i = property.getSlot();
@@ -3274,7 +3272,6 @@
}
final Property prop = find.getProperty();
- notifyPropertyDeleted(this, prop);
deleteOwnProperty(prop);
return true;
--- a/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java Mon Mar 03 15:23:01 2014 +0100
@@ -80,7 +80,7 @@
}
/**
- * This class encapsulates the results of looking up a setter method; it's basically a triple of a method hanle,
+ * This class encapsulates the results of looking up a setter method; it's basically a triple of a method handle,
* a Property object, and flags for invocation.
*
*/
@@ -170,7 +170,10 @@
private SetMethod createNewPropertySetter() {
final SetMethod sm = map.getFieldCount() < map.getFieldMaximum() ? createNewFieldSetter() : createNewSpillPropertySetter();
- sobj.notifyPropertyAdded(sobj, sm.property);
+ final PropertyListeners listeners = map.getListeners();
+ if (listeners != null) {
+ listeners.propertyAdded(sm.property);
+ }
return sm;
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/WithObject.java Mon Mar 03 15:23:01 2014 +0100
@@ -36,6 +36,7 @@
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
+import jdk.nashorn.internal.runtime.linker.NashornGuards;
/**
* This class supports the handling of scope in a with body.
@@ -123,7 +124,7 @@
}
if (find != null) {
- return fixScopeCallSite(scope.lookup(desc, request), name);
+ return fixScopeCallSite(scope.lookup(desc, request), name, find.getOwner());
}
// the property is not found - now check for
@@ -175,7 +176,7 @@
link = scope.lookup(desc, request);
if (link != null) {
- return fixScopeCallSite(link, name);
+ return fixScopeCallSite(link, name, null);
}
return null;
@@ -252,13 +253,10 @@
filterGuard(link, WITHEXPRESSIONFILTER));
}
- private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name) {
+ private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name, final ScriptObject owner) {
final GuardedInvocation newLink = fixReceiverType(link, WITHSCOPEFILTER);
return link.replaceMethods(filter(newLink.getInvocation(), WITHSCOPEFILTER),
- MH.guardWithTest(
- expressionGuard(name),
- filterGuard(newLink, WITHSCOPEFILTER),
- MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class)));
+ NashornGuards.combineGuards(expressionGuard(name, owner), filterGuard(newLink, WITHSCOPEFILTER)));
}
private static MethodHandle filterGuard(final GuardedInvocation link, final MethodHandle filter) {
@@ -288,9 +286,9 @@
return fn.makeBoundFunction(withFilterExpression(receiver), new Object[0]);
}
- private MethodHandle expressionGuard(final String name) {
+ private MethodHandle expressionGuard(final String name, final ScriptObject owner) {
final PropertyMap map = expression.getMap();
- final SwitchPoint sp = map.getProtoGetSwitchPoint(expression.getProto(), name);
+ final SwitchPoint sp = expression.getProtoSwitchPoint(name, owner);
return MH.insertArguments(WITHEXPRESSIONGUARD, 1, map, sp);
}
--- a/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java Mon Mar 03 15:23:01 2014 +0100
@@ -37,10 +37,10 @@
* Constructor of method handles used to guard call sites.
*/
public final class NashornGuards {
- private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class);
- private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class);
- private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
- private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
+ private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class);
+ private static final MethodHandle IS_SCRIPTFUNCTION = findOwnMH("isScriptFunction", boolean.class, Object.class);
+ private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
+ private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
// don't create me!
private NashornGuards() {
@@ -85,6 +85,17 @@
return MH.insertArguments(IS_INSTANCEOF_2, 1, class1, class2);
}
+ /**
+ * Combine two method handles of type {@code (Object)boolean} using logical AND.
+ *
+ * @param guard1 the first guard
+ * @param guard2 the second guard, only invoked if guard1 returns true
+ * @return true if both guard1 and guard2 returned true
+ */
+ public static MethodHandle combineGuards(final MethodHandle guard1, final MethodHandle guard2) {
+ return MH.guardWithTest(guard1, guard2, MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class));
+ }
+
@SuppressWarnings("unused")
private static boolean isScriptObject(final Object self) {
return self instanceof ScriptObject;
--- a/nashorn/src/jdk/nashorn/internal/scripts/JO.java Tue Feb 25 18:56:10 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/scripts/JO.java Mon Mar 03 15:23:01 2014 +0100
@@ -33,7 +33,7 @@
*/
public class JO extends ScriptObject {
- private static final PropertyMap map$ = PropertyMap.newMap().setIsShared();
+ private static final PropertyMap map$ = PropertyMap.newMap();
/**
* Returns the initial property map to be used.