8035948: Redesign property listeners for shared classes
authorhannesw
Mon, 03 Mar 2014 15:23:01 +0100
changeset 23084 6c5c02d1023a
parent 23083 8c74590d5df1
child 23085 7825ce9a8228
8035948: Redesign property listeners for shared classes Reviewed-by: sundar, lagergren
nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java
nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ConstructorGenerator.java
nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/PrototypeGenerator.java
nashorn/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
nashorn/src/jdk/nashorn/internal/codegen/ConstantData.java
nashorn/src/jdk/nashorn/internal/objects/AccessorPropertyDescriptor.java
nashorn/src/jdk/nashorn/internal/objects/ArrayBufferView.java
nashorn/src/jdk/nashorn/internal/objects/DataPropertyDescriptor.java
nashorn/src/jdk/nashorn/internal/objects/GenericPropertyDescriptor.java
nashorn/src/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk/nashorn/internal/objects/NativeArguments.java
nashorn/src/jdk/nashorn/internal/objects/NativeArray.java
nashorn/src/jdk/nashorn/internal/objects/NativeArrayBuffer.java
nashorn/src/jdk/nashorn/internal/objects/NativeBoolean.java
nashorn/src/jdk/nashorn/internal/objects/NativeDate.java
nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java
nashorn/src/jdk/nashorn/internal/objects/NativeError.java
nashorn/src/jdk/nashorn/internal/objects/NativeEvalError.java
nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
nashorn/src/jdk/nashorn/internal/objects/NativeJavaImporter.java
nashorn/src/jdk/nashorn/internal/objects/NativeNumber.java
nashorn/src/jdk/nashorn/internal/objects/NativeRangeError.java
nashorn/src/jdk/nashorn/internal/objects/NativeReferenceError.java
nashorn/src/jdk/nashorn/internal/objects/NativeRegExp.java
nashorn/src/jdk/nashorn/internal/objects/NativeRegExpExecResult.java
nashorn/src/jdk/nashorn/internal/objects/NativeStrictArguments.java
nashorn/src/jdk/nashorn/internal/objects/NativeString.java
nashorn/src/jdk/nashorn/internal/objects/NativeSyntaxError.java
nashorn/src/jdk/nashorn/internal/objects/NativeTypeError.java
nashorn/src/jdk/nashorn/internal/objects/NativeURIError.java
nashorn/src/jdk/nashorn/internal/objects/PrototypeObject.java
nashorn/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyListener.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyListenerManager.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyListeners.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java
nashorn/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunction.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java
nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
nashorn/src/jdk/nashorn/internal/runtime/WithObject.java
nashorn/src/jdk/nashorn/internal/runtime/linker/NashornGuards.java
nashorn/src/jdk/nashorn/internal/scripts/JO.java
--- 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.