8006220: Simplify PropertyMaps
authorjlaskey
Tue, 30 Apr 2013 10:05:42 -0300
changeset 17513 b9a691fc1df5
parent 17259 a8d0cb25d725
child 17514 fed0d7b5d9f6
child 17515 953eaa0cb8d5
8006220: Simplify PropertyMaps Reviewed-by: hannesw, lagergren Contributed-by: james.laskey@oracle.com
nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java
nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java
nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java
nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java
nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java
nashorn/src/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk/nashorn/internal/runtime/Property.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyHashMap.java
nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java
nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java
nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java
nashorn/src/jdk/nashorn/internal/runtime/StructureLoader.java
nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
nashorn/src/jdk/nashorn/internal/scripts/JO.java
nashorn/src/jdk/nashorn/tools/Shell.java
--- a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java	Tue Apr 30 10:05:42 2013 -0300
@@ -65,10 +65,12 @@
      * Constructs a property map based on a set of fields.
      *
      * @param hasArguments does the created object have an "arguments" property
+     * @param fieldCount    Number of fields in use.
+     * @param fieldMaximum Number of fields available.
      *
      * @return New map populated with accessor properties.
      */
-    PropertyMap makeMap(final boolean hasArguments) {
+    PropertyMap makeMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
         final List<Property> properties = new ArrayList<>();
 
         assert keys != null;
@@ -82,7 +84,7 @@
             }
         }
 
-        return PropertyMap.newMap(structure, properties);
+        return PropertyMap.newMap(structure, properties, fieldCount, fieldMaximum);
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectClassGenerator.java	Tue Apr 30 10:05:42 2013 -0300
@@ -69,6 +69,16 @@
     static final String SCOPE_MARKER = "P";
 
     /**
+     * Minimum number of extra fields in an object.
+     */
+    static final int FIELD_PADDING  = 4;
+
+    /**
+     * Rounding when calculating the number of fields.
+     */
+    static final int FIELD_ROUNDING = 4;
+
+    /**
      * Debug field logger
      * Should we print debugging information for fields when they are generated and getters/setters are called?
      */
--- a/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/ObjectCreator.java	Tue Apr 30 10:05:42 2013 -0300
@@ -26,6 +26,8 @@
 package jdk.nashorn.internal.codegen;
 
 import java.util.List;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_PADDING;
+import static jdk.nashorn.internal.codegen.ObjectClassGenerator.FIELD_ROUNDING;
 import jdk.nashorn.internal.ir.Symbol;
 import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.PropertyMap;
@@ -50,6 +52,7 @@
     private   final boolean       isScope;
     private   final boolean       hasArguments;
     private         int           fieldCount;
+    private         int           paddedFieldCount;
     private         int           paramCount;
     private         String        fieldObjectClassName;
     private         Class<?>      fieldObjectClass;
@@ -88,6 +91,8 @@
                 }
             }
         }
+
+        paddedFieldCount = (fieldCount + FIELD_PADDING + FIELD_ROUNDING - 1) / FIELD_ROUNDING * FIELD_ROUNDING;
     }
 
     /**
@@ -96,7 +101,7 @@
     private void findClass() {
         fieldObjectClassName = isScope() ?
             ObjectClassGenerator.getClassName(fieldCount, paramCount) :
-            ObjectClassGenerator.getClassName(fieldCount);
+            ObjectClassGenerator.getClassName(paddedFieldCount);
 
         try {
             this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
@@ -125,11 +130,7 @@
      * @return the newly created property map
      */
     protected PropertyMap makeMap() {
-        if (keys.isEmpty()) { //empty map
-            propertyMap = PropertyMap.newMap(fieldObjectClass);
-        } else {
-            propertyMap = newMapCreator(fieldObjectClass).makeMap(hasArguments());
-        }
+        propertyMap = newMapCreator(fieldObjectClass).makeMap(hasArguments(), fieldCount, paddedFieldCount);
         return propertyMap;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeDebug.java	Tue Apr 30 10:05:42 2013 -0300
@@ -87,66 +87,6 @@
     }
 
     /**
-     * Nashorn extension: get embed0 from {@link ScriptObject}
-     *
-     * @param self self reference
-     * @param obj script object
-     * @return the embed0 property value for the given ScriptObject
-     */
-    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
-    public static Object embed0(final Object self, final Object obj) {
-        if (obj instanceof ScriptObject) {
-            return ((ScriptObject)obj).embed0;
-        }
-        return UNDEFINED;
-    }
-
-    /**
-     * Nashorn extension: get embed1 from {@link ScriptObject}
-     *
-     * @param self self reference
-     * @param obj script object
-     * @return the embed1 property value for the given ScriptObject
-     */
-    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
-    public static Object embed1(final Object self, final Object obj) {
-        if (obj instanceof ScriptObject) {
-            return ((ScriptObject)obj).embed1;
-        }
-        return UNDEFINED;
-    }
-
-    /**
-     * Nashorn extension: get embed2 from {@link ScriptObject}
-     *
-     * @param self self reference
-     * @param obj script object
-     * @return the embed2 property value for the given ScriptObject
-     */
-    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
-    public static Object embed2(final Object self, final Object obj) {
-        if (obj instanceof ScriptObject) {
-            return ((ScriptObject)obj).embed2;
-        }
-        return UNDEFINED;
-    }
-
-    /**
-     * Nashorn extension: get embed3 from {@link ScriptObject}
-     *
-     * @param self self reference
-     * @param obj script object
-     * @return the embed3 property value for the given ScriptObject
-     */
-    @Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
-    public static Object embed3(final Object self, final Object obj) {
-        if (obj instanceof ScriptObject) {
-            return ((ScriptObject)obj).embed3;
-        }
-        return UNDEFINED;
-    }
-
-    /**
      * Nashorn extension: get spill vector from {@link ScriptObject}
      *
      * @param self self reference
--- a/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/NativeJSAdapter.java	Tue Apr 30 10:05:42 2013 -0300
@@ -620,7 +620,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(__call__), testJSAdaptor(adaptee, null, null, null));
+                        adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), __call__), testJSAdaptor(adaptee, null, null, null));
             }
             throw typeError("no.such.function", desc.getNameToken(2), ScriptRuntime.safeToString(this));
         default:
@@ -687,7 +687,7 @@
             if (methodHandle != null) {
                 return new GuardedInvocation(
                         methodHandle,
-                        adaptee.getMap().getProtoGetSwitchPoint(hook),
+                        adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook),
                         testJSAdaptor(adaptee, findData.getGetter(Object.class), findData.getOwner(), func));
             }
         }
@@ -699,7 +699,7 @@
             final MethodHandle methodHandle = hook.equals(__put__) ?
             MH.asType(Lookup.EMPTY_SETTER, type) :
             Lookup.emptyGetter(type.returnType());
-            return new GuardedInvocation(methodHandle, adaptee.getMap().getProtoGetSwitchPoint(hook), testJSAdaptor(adaptee, null, null, null));
+            return new GuardedInvocation(methodHandle, adaptee.getMap().getProtoGetSwitchPoint(adaptee.getProto(), hook), testJSAdaptor(adaptee, null, null, null));
         }
     }
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/AccessorProperty.java	Tue Apr 30 10:05:42 2013 -0300
@@ -50,8 +50,6 @@
 /**
  * An AccessorProperty is the most generic property type. An AccessorProperty is
  * represented as fields in a ScriptObject class.
- *
- * @see SpillProperty
  */
 public class AccessorProperty extends Property {
     private static final MethodHandles.Lookup lookup = MethodHandles.lookup();
@@ -77,6 +75,7 @@
 
     private static final MethodType[] ACCESSOR_GETTER_TYPES = new MethodType[NOOF_TYPES];
     private static final MethodType[] ACCESSOR_SETTER_TYPES = new MethodType[NOOF_TYPES];
+    private static final MethodHandle SPILLGETTER = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), Lookup.GET_OBJECT_TYPE);
 
     /** Seed getter for the primitive version of this field (in -Dnashorn.fields.dual=true mode) */
     private MethodHandle primitiveGetter;
@@ -285,7 +284,7 @@
                 "get");
         }
 
-        return getters[i];
+        return isSpill() ? MH.filterArguments(getters[i], 0, SPILLGETTER) : getters[i];
     }
 
     private Property getWiderProperty(final Class<?> type) {
@@ -327,6 +326,7 @@
         final Class<?> forType = currentType == null ? type : currentType;
 
         //if we are asking for an object setter, but are still a primitive type, we might try to box it
+        MethodHandle mh;
 
         if (needsInvalidator(i, ci)) {
             final Property     newProperty = getWiderProperty(type);
@@ -335,12 +335,15 @@
             final MethodHandle explodeTypeSetter = MH.filterArguments(widerSetter, 0, MH.insertArguments(REPLACE_MAP, 1, newMap, getKey(), currentType, type));
             if (currentType != null && currentType.isPrimitive() && type == Object.class) {
                 //might try a box check on this to avoid widening field to object storage
-                return createGuardBoxedPrimitiveSetter(currentType, generateSetter(currentType, currentType), explodeTypeSetter);
+                mh = createGuardBoxedPrimitiveSetter(currentType, generateSetter(currentType, currentType), explodeTypeSetter);
+            } else {
+                mh = explodeTypeSetter;
             }
-            return explodeTypeSetter;
+        } else {
+            mh = generateSetter(forType, type);
         }
 
-        return generateSetter(forType, type);
+        return isSpill() ? MH.filterArguments(mh, 0, SPILLGETTER) : mh;
     }
 
     @Override
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue Apr 30 10:05:42 2013 -0300
@@ -201,9 +201,6 @@
     /** Current error manager. */
     private final ErrorManager errors;
 
-    /** Empty map used for seed map for JO objects */
-    final PropertyMap emptyMap = PropertyMap.newEmptyMap(this);
-
     private static final ClassLoader myLoader = Context.class.getClassLoader();
     private static final StructureLoader sharedLoader;
 
--- a/nashorn/src/jdk/nashorn/internal/runtime/Property.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Property.java	Tue Apr 30 10:05:42 2013 -0300
@@ -41,7 +41,6 @@
  *
  * @see PropertyMap
  * @see AccessorProperty
- * @see SpillProperty
  * @see UserAccessorProperty
  */
 public abstract class Property {
@@ -64,7 +63,7 @@
 
     private static final int MODIFY_MASK     = 0b0000_0000_1111;
 
-    /** Is this a spill property? See {@link SpillProperty} */
+    /** Is this a spill property? See {@link AccessorProperty} */
     public static final int IS_SPILL         = 0b0000_0001_0000;
 
     /** Is this a function parameter? */
@@ -88,7 +87,7 @@
     /** Property flags. */
     protected int flags;
 
-    /** Property field number or spill slot */
+    /** Property field number or spill slot. */
     private final int slot;
 
     /**
@@ -248,7 +247,7 @@
      * Does this property use any slots in the spill array described in
      * {@link Property#isSpill}? In that case how many. Currently a property
      * only uses max one spill slot, but this may change in future representations
-     * Only {@link SpillProperty} instances use spill slots
+     * Only {@link AccessorProperty} instances use spill slots
      *
      * @return number of spill slots a property is using
      */
@@ -345,6 +344,14 @@
     }
 
     /**
+     * Get the field number or spill slot
+     * @return number/slot, -1 if none exists
+     */
+    public int getSlot() {
+        return slot;
+    }
+
+    /**
      * Abstract method for retrieving the setter for the property. We do not know
      * anything about the internal representation when we request the setter, we only
      * know that the setter will take the property as a parameter of the given type.
@@ -388,14 +395,6 @@
         return null;
     }
 
-    /**
-     * Get the field number or spill slot
-     * @return number/slot, -1 if none exists
-     */
-    public int getSlot() {
-        return slot;
-    }
-
     @Override
     public int hashCode() {
         final Class<?> type = getCurrentType();
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyHashMap.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyHashMap.java	Tue Apr 30 10:05:42 2013 -0300
@@ -110,7 +110,7 @@
     private static final int LIST_THRESHOLD = 8;
 
     /** Initial map. */
-    public static final PropertyHashMap EMPTY_MAP = new PropertyHashMap();
+    public static final PropertyHashMap EMPTY_HASHMAP = new PropertyHashMap();
 
     /** Number of properties in the map. */
     private final int size;
@@ -246,7 +246,7 @@
             }
         } else if (findElement(list, key) != null) {
             final int newSize = size - 1;
-            return newSize != 0 ? new PropertyHashMap(newSize, null, removeFromList(list, key)) : EMPTY_MAP;
+            return newSize != 0 ? new PropertyHashMap(newSize, null, removeFromList(list, key)) : EMPTY_HASHMAP;
         }
         return this;
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/PropertyMap.java	Tue Apr 30 10:05:42 2013 -0300
@@ -25,7 +25,7 @@
 
 package jdk.nashorn.internal.runtime;
 
-import static jdk.nashorn.internal.runtime.PropertyHashMap.EMPTY_MAP;
+import static jdk.nashorn.internal.runtime.PropertyHashMap.EMPTY_HASHMAP;
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.SwitchPoint;
@@ -49,29 +49,27 @@
  * will return a new map.
  */
 public final class PropertyMap implements Iterable<Object>, PropertyListener {
-    /** Is this a prototype PropertyMap? */
-    public static final int IS_PROTOTYPE          = 0b0000_0001;
     /** Used for non extensible PropertyMaps, negative logic as the normal case is extensible. See {@link ScriptObject#preventExtensions()} */
-    public static final int NOT_EXTENSIBLE        = 0b0000_0010;
+    public static final int NOT_EXTENSIBLE        = 0b0000_0001;
     /** 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;
 
+    /** Empty map used for seed map for JO$ objects */
+    private static final PropertyMap EMPTY_MAP = new PropertyMap(EMPTY_HASHMAP);
+
     /** Map status flags. */
     private int flags;
 
-    /** Class of object referenced.*/
-    private final Class<?> structure;
-
-    /** Context associated with this {@link PropertyMap}. */
-    private final Context context;
-
     /** Map of properties. */
     private final PropertyHashMap properties;
 
-    /** objects proto. */
-    private ScriptObject proto;
+    /** Number of fields in use. */
+    private int fieldCount;
+
+    /** Number of fields available. */
+    private int fieldMaximum;
 
     /** Length of spill in use. */
     private int spillLength;
@@ -91,15 +89,15 @@
     /**
      * Constructor.
      *
-     * @param structure  Class the map's {@link AccessorProperty}s apply to.
-     * @param context    Context associated with this {@link PropertyMap}.
-     * @param properties A {@link PropertyHashMap} with initial contents.
+     * @param properties    A {@link PropertyHashMap} with initial contents.
+     * @param fieldCount    Number of fields in use.
+     * @param fieldMaximum Number of fields available.
      */
-    PropertyMap(final Class<?> structure, final Context context, final PropertyHashMap properties) {
-        this.structure  = structure;
-        this.context    = context;
-        this.properties = properties;
-        this.hashCode   = computeHashCode();
+    private PropertyMap(final PropertyHashMap properties, final int fieldCount, final int fieldMaximum) {
+        this.properties   = properties;
+        this.hashCode     = computeHashCode();
+        this.fieldCount   = fieldCount;
+        this.fieldMaximum = fieldMaximum;
 
         if (Context.DEBUG) {
             count++;
@@ -107,19 +105,27 @@
     }
 
     /**
+     * Constructor.
+     *
+     * @param properties A {@link PropertyHashMap} with initial contents.
+     */
+    private PropertyMap(final PropertyHashMap properties) {
+        this(properties, 0, 0);
+    }
+
+    /**
      * Cloning constructor.
      *
      * @param propertyMap Existing property map.
      * @param properties  A {@link PropertyHashMap} with a new set of properties.
      */
     private PropertyMap(final PropertyMap propertyMap, final PropertyHashMap properties) {
-        this.structure   = propertyMap.structure;
-        this.context     = propertyMap.context;
-        this.properties  = properties;
-        this.flags       = propertyMap.getClonedFlags();
-        this.proto       = propertyMap.proto;
-        this.spillLength = propertyMap.spillLength;
-        this.hashCode    = computeHashCode();
+        this.properties   = properties;
+        this.flags        = propertyMap.getClonedFlags();
+        this.spillLength  = propertyMap.spillLength;
+        this.fieldCount   = propertyMap.fieldCount;
+        this.fieldMaximum = propertyMap.fieldMaximum;
+        this.hashCode     = computeHashCode();
 
         if (Context.DEBUG) {
             count++;
@@ -128,6 +134,15 @@
     }
 
     /**
+     * Cloning constructor.
+     *
+     * @param propertyMap Existing property map.
+      */
+    private PropertyMap(final PropertyMap propertyMap) {
+        this(propertyMap, propertyMap.properties);
+    }
+
+    /**
      * Duplicates this PropertyMap instance. This is used by nasgen generated
      * prototype and constructor classes. {@link PropertyMap} used for singletons
      * like these (and global instance) are duplicated using this method and used.
@@ -138,7 +153,7 @@
      * @return Duplicated {@link PropertyMap}.
      */
     public PropertyMap duplicate() {
-        return new PropertyMap(this.structure, this.context, this.properties);
+        return new PropertyMap(this.properties);
     }
 
     /**
@@ -146,20 +161,20 @@
      *
      * @param structure  Class the map's {@link AccessorProperty}s apply to.
      * @param properties Collection of initial properties.
+     * @param fieldCount    Number of fields in use.
+     * @param fieldMaximum Number of fields available.
      *
      * @return New {@link PropertyMap}.
      */
-    public static PropertyMap newMap(final Class<?> structure, final Collection<Property> properties) {
-        final Context context = Context.fromClass(structure);
-
+    public static PropertyMap newMap(final Class<?> structure, final Collection<Property> properties, final int fieldCount, final int fieldMaximum) {
         // Reduce the number of empty maps in the context.
         if (structure == jdk.nashorn.internal.scripts.JO.class) {
-            return context.emptyMap;
+            return EMPTY_MAP;
         }
 
-        PropertyHashMap newProperties = EMPTY_MAP.immutableAdd(properties);
+        PropertyHashMap newProperties = EMPTY_HASHMAP.immutableAdd(properties);
 
-        return new PropertyMap(structure, context, newProperties);
+        return new PropertyMap(newProperties, fieldCount, fieldMaximum);
     }
 
     /**
@@ -170,7 +185,7 @@
      * @return New {@link PropertyMap}.
      */
     public static PropertyMap newMap(final Class<?> structure) {
-        return newMap(structure, null);
+        return newMap(structure, null, 0, 0);
     }
 
     /**
@@ -180,7 +195,7 @@
      * @return New empty {@link PropertyMap}.
      */
     public static PropertyMap newEmptyMap(final Context context) {
-        return new PropertyMap(jdk.nashorn.internal.scripts.JO.class, context, EMPTY_MAP);
+        return new PropertyMap(EMPTY_HASHMAP);
     }
 
     /**
@@ -195,11 +210,12 @@
     /**
      * Return a SwitchPoint used to track changes of a property in a prototype.
      *
-     * @param key {@link Property} key.
+     * @param proto  Object prototype.
+     * @param key    {@link Property} key.
      *
      * @return A shared {@link SwitchPoint} for the property.
      */
-    public SwitchPoint getProtoGetSwitchPoint(final String key) {
+    public SwitchPoint getProtoGetSwitchPoint(final ScriptObject proto, final String key) {
         if (proto == null) {
             return null;
         }
@@ -295,6 +311,11 @@
             final PropertyHashMap newProperties = properties.immutableAdd(property);
             newMap = new PropertyMap(this, newProperties);
             addToHistory(property, newMap);
+
+            if(!property.isSpill()) {
+                newMap.fieldCount = Math.max(newMap.fieldCount, property.getSlot() + 1);
+            }
+
             newMap.spillLength += property.getSpillCount();
         }
 
@@ -355,7 +376,6 @@
                 newProperty instanceof UserAccessorProperty) : "arbitrary replaceProperty attempted";
 
         newMap.flags = getClonedFlags();
-        newMap.proto = proto;
 
         /*
          * spillLength remains same in case (1) and (2) because of slot reuse. Only for case (3), we need
@@ -411,7 +431,7 @@
      * @return New map with {@link #NOT_EXTENSIBLE} flag set.
      */
     PropertyMap preventExtensions() {
-        final PropertyMap newMap = new PropertyMap(this, this.properties);
+        final PropertyMap newMap = new PropertyMap(this);
         newMap.flags |= NOT_EXTENSIBLE;
         return newMap;
     }
@@ -423,7 +443,7 @@
      * {@link Property#NOT_CONFIGURABLE} set.
      */
     PropertyMap seal() {
-        PropertyHashMap newProperties = EMPTY_MAP;
+        PropertyHashMap newProperties = EMPTY_HASHMAP;
 
         for (final Property oldProperty :  properties.getProperties()) {
             newProperties = newProperties.immutableAdd(oldProperty.addFlags(Property.NOT_CONFIGURABLE));
@@ -442,7 +462,7 @@
      * {@link Property#NOT_CONFIGURABLE} and {@link Property#NOT_WRITABLE} set.
      */
     PropertyMap freeze() {
-        PropertyHashMap newProperties = EMPTY_MAP;
+        PropertyHashMap newProperties = EMPTY_HASHMAP;
 
         for (Property oldProperty : properties.getProperties()) {
             int propertyFlags = Property.NOT_CONFIGURABLE;
@@ -578,11 +598,7 @@
      * @return Computed hash code.
      */
     private int computeHashCode() {
-        int hash = structure.hashCode();
-
-        if (proto != null) {
-            hash ^= proto.hashCode();
-        }
+        int hash = 0;
 
         for (final Property property : getProperties()) {
             hash = hash << 7 ^ hash >> 7;
@@ -605,9 +621,7 @@
 
         final PropertyMap otherMap = (PropertyMap)other;
 
-        if (structure != otherMap.structure ||
-            proto != otherMap.proto ||
-            properties.size() != otherMap.properties.size()) {
+        if (properties.size() != otherMap.properties.size()) {
             return false;
         }
 
@@ -659,31 +673,6 @@
     }
 
     /**
-     * Return map's {@link Context}.
-     *
-     * @return The {@link Context} where the map originated.
-     */
-    Context getContext() {
-        return context;
-    }
-
-    /**
-     * Check if this map is a prototype
-     *
-     * @return {@code true} if is prototype
-     */
-    public boolean isPrototype() {
-        return (flags & IS_PROTOTYPE) != 0;
-    }
-
-    /**
-     * Flag this map as having a prototype.
-     */
-    private void setIsPrototype() {
-        flags |= IS_PROTOTYPE;
-    }
-
-    /**
      * Check whether a {@link PropertyListener} has been added to this map.
      *
      * @return {@code true} if {@link PropertyListener} exists
@@ -720,6 +709,22 @@
     boolean isFrozen() {
         return !isExtensible() && allFrozen();
     }
+    /**
+     * Get the number of fields allocated for this {@link PropertyMap}.
+     *
+     * @return Number of fields allocated.
+     */
+    int getFieldCount() {
+        return fieldCount;
+    }
+    /**
+     * Get maximum number of fields available for this {@link PropertyMap}.
+     *
+     * @return Number of fields available.
+     */
+    int getFieldMaximum() {
+        return fieldMaximum;
+    }
 
     /**
      * Get length of spill area associated with this {@link PropertyMap}.
@@ -731,25 +736,20 @@
     }
 
     /**
-     * Return the prototype of objects associated with this {@link PropertyMap}.
+     * Change the prototype of objects associated with this {@link PropertyMap}.
      *
-     * @return Prototype object.
-     */
-    ScriptObject getProto() {
-        return proto;
-    }
-
-    /**
-     * Set the prototype of objects associated with this {@link PropertyMap}.
-     *
-     * @param newProto Prototype object to use.
+     * @param oldProto Current prototype object.
+     * @param newProto New prototype object to replace oldProto.
      *
      * @return New {@link PropertyMap} with prototype changed.
      */
-    PropertyMap setProto(final ScriptObject newProto) {
-        final ScriptObject oldProto = this.proto;
-
-        if (oldProto == newProto) {
+    PropertyMap changeProto(final ScriptObject oldProto, final ScriptObject newProto) {
+        if ((oldProto == newProto) ||
+            (size() == 0 &&
+             oldProto == null &&
+             protoGetSwitches == null &&
+             history == null &&
+             protoHistory == null)) {
             return this;
         }
 
@@ -761,19 +761,10 @@
         if (Context.DEBUG) {
             incrementSetProtoNewMapCount();
         }
-        final PropertyMap newMap = new PropertyMap(this, this.properties);
+
+        final PropertyMap newMap = new PropertyMap(this);
         addToProtoHistory(newProto, newMap);
 
-        newMap.proto = newProto;
-
-        if (oldProto != null && newMap.isListenerAdded()) {
-            oldProto.removePropertyListener(newMap);
-        }
-
-        if (newProto != null) {
-            newProto.getMap().setIsPrototype();
-        }
-
         return newMap;
     }
 
@@ -927,4 +918,3 @@
         setProtoNewMapCount++;
     }
 }
-
--- a/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/ScriptObject.java	Tue Apr 30 10:05:42 2013 -0300
@@ -104,34 +104,31 @@
     /** 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;
+
     /** Spill growth rate - by how many elements does {@link ScriptObject#spill} when full */
     public static final int SPILL_RATE = 8;
 
     /** Map to property information and accessor functions. Ordered by insertion. */
     private PropertyMap map;
 
+    /** objects proto. */
+    private ScriptObject proto;
+
+    /** Context of the object, lazily cached. */
+    private Context context;
+
     /** Object flags. */
     private int flags;
 
-    /** Area for properties added to object after instantiation, see {@link SpillProperty} */
+    /** Area for properties added to object after instantiation, see {@link AccessorProperty} */
     public Object[] spill;
 
-    /** Local embed area position 0 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
-    public Object embed0;
-
-    /** Local embed area position 1 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
-    public Object embed1;
-
-    /** Local embed area position 2 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
-    public Object embed2;
-
-    /** Local embed area position 3 - used for {@link SpillProperty} before {@link ScriptObject#spill} */
-    public Object embed3;
-
     /** Indexed array data. */
     private ArrayData arrayData;
 
-    static final MethodHandle SETEMBED           = findOwnMH("setEmbed",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, int.class, Object.class, Object.class);
+    static final MethodHandle SETFIELD           = findOwnMH("setField",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, MethodHandle.class, Object.class, Object.class);
     static final MethodHandle SETSPILL           = findOwnMH("setSpill",         void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
     static final MethodHandle SETSPILLWITHNEW    = findOwnMH("setSpillWithNew",  void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, Object.class, Object.class);
     static final MethodHandle SETSPILLWITHGROW   = findOwnMH("setSpillWithGrow", void.class, CallSiteDescriptor.class, PropertyMap.class, PropertyMap.class, int.class, int.class, Object.class, Object.class);
@@ -783,8 +780,8 @@
                 // delete getter and setter function references so that we don't leak
                 if (property instanceof UserAccessorProperty) {
                     final UserAccessorProperty uc = (UserAccessorProperty) property;
-                    setEmbedOrSpill(uc.getGetterSlot(), null);
-                    setEmbedOrSpill(uc.getSetterSlot(), null);
+                    setSpill(uc.getGetterSlot(), null);
+                    setSpill(uc.getSetterSlot(), null);
                 }
                 return true;
             }
@@ -809,7 +806,7 @@
 
             int getterSlot = uc.getGetterSlot();
             // clear the old getter and set the new getter
-            setEmbedOrSpill(getterSlot, getter);
+            setSpill(getterSlot, getter);
             // if getter function is null, flag the slot to be negative (less by 1)
             if (getter == null) {
                 getterSlot = -getterSlot - 1;
@@ -817,7 +814,7 @@
 
             int setterSlot = uc.getSetterSlot();
             // clear the old setter and set the new setter
-            setEmbedOrSpill(setterSlot, setter);
+            setSpill(setterSlot, setter);
             // if setter function is null, flag the slot to be negative (less by 1)
             if (setter == null) {
                 setterSlot = -setterSlot - 1;
@@ -1056,8 +1053,11 @@
      * Return the current context from the object's map.
      * @return Current context.
      */
-    final Context getContext() {
-        return getMap().getContext();
+    protected final Context getContext() {
+        if (context == null) {
+            context = Context.fromClass(getClass());
+        }
+        return context;
     }
 
     /**
@@ -1097,44 +1097,30 @@
      * @return __proto__ object.
      */
     public final ScriptObject getProto() {
-        return getMap().getProto();
-    }
-
-    /**
-     * Check if this is a prototype
-     * @return true if {@link PropertyMap#isPrototype()} is true for this ScriptObject
-     */
-    public final boolean isPrototype() {
-        return getMap().isPrototype();
+        return proto;
     }
 
     /**
      * Set the __proto__ of an object.
      * @param newProto new __proto__ to set.
      */
-    public final void setProto(final ScriptObject newProto) {
-        PropertyMap  oldMap   = getMap();
-        ScriptObject oldProto = getProto();
-
-        while (oldProto != newProto) {
-            final PropertyMap newMap = oldMap.setProto(newProto);
-
-            if (!compareAndSetMap(oldMap, newMap)) {
-                oldMap = getMap();
-                oldProto = getProto();
-            } else {
-                if (isPrototype()) {
-
-                    if (oldProto != null) {
-                        oldProto.removePropertyListener(this);
-                    }
-
-                    if (newProto != null) {
-                        newProto.addPropertyListener(this);
-                    }
-                }
-
-                return;
+    public synchronized final void setProto(final ScriptObject newProto) {
+        final ScriptObject oldProto = proto;
+        map = map.changeProto(oldProto, newProto);
+
+        if (newProto != null) {
+            newProto.setIsPrototype();
+        }
+
+        proto = newProto;
+
+        if (isPrototype()) {
+            if (oldProto != null) {
+                oldProto.removePropertyListener(this);
+            }
+
+            if (newProto != null) {
+                newProto.addPropertyListener(this);
             }
         }
     }
@@ -1327,6 +1313,22 @@
     }
 
     /**
+     * Check if this object is a prototype
+     *
+     * @return {@code true} if is prototype
+     */
+    public boolean isPrototype() {
+        return (flags & IS_PROTOTYPE) != 0;
+    }
+
+    /**
+     * Flag this object as having a prototype.
+     */
+    public void setIsPrototype() {
+        flags |= IS_PROTOTYPE;
+    }
+
+    /**
      * Get the {@link ArrayData} for this ScriptObject if it is an array
      * @return array data
      */
@@ -1719,11 +1721,11 @@
             if (!property.hasGetterFunction()) {
                 methodHandle = bindTo(methodHandle, prototype);
             }
-            return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(name), guard);
+            return new GuardedInvocation(methodHandle, getMap().getProtoGetSwitchPoint(proto, name), guard);
         }
 
         assert !NashornCallSiteDescriptor.isFastScope(desc);
-        return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(name), guard);
+        return new GuardedInvocation(Lookup.emptyGetter(returnType), getMap().getProtoGetSwitchPoint(proto, name), guard);
     }
 
     private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name) {
@@ -1822,27 +1824,31 @@
            }
            assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
            final PropertyMap myMap = getMap();
-           return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(myMap));
+           return new GuardedInvocation(Lookup.EMPTY_SETTER, myMap.getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(myMap));
     }
 
     @SuppressWarnings("unused")
-    private static void setEmbed(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final int i, final Object self, final Object value) throws Throwable {
+    private static void setField(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final MethodHandle setter, final Object self, final Object value) throws Throwable {
         final ScriptObject obj = (ScriptObject)self;
-        if (obj.trySetEmbedOrSpill(desc, oldMap, newMap, value)) {
-            obj.useEmbed(i);
+        final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
+        if (!obj.isExtensible()) {
+            throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(obj));
+        } else if (obj.compareAndSetMap(oldMap, newMap)) {
             setter.invokeExact(self, value);
+        } else {
+            obj.set(desc.getNameToken(CallSiteDescriptor.NAME_OPERAND), value, isStrict);
         }
     }
 
     @SuppressWarnings("unused")
     private static void setSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final int index, final Object self, final Object value) {
         final ScriptObject obj = (ScriptObject)self;
-        if (obj.trySetEmbedOrSpill(desc, oldMap, newMap, value)) {
+        if (obj.trySetSpill(desc, oldMap, newMap, value)) {
             obj.spill[index] = value;
         }
     }
 
-    private boolean trySetEmbedOrSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
+    private boolean trySetSpill(final CallSiteDescriptor desc, final PropertyMap oldMap, final PropertyMap newMap, final Object value) {
         final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
         if (!isExtensible() && isStrict) {
             throw typeError("object.non.extensible", desc.getNameToken(2), ScriptRuntime.safeToString(this));
@@ -1964,7 +1970,7 @@
                     methodHandle = bindTo(methodHandle, UNDEFINED);
                 }
                 return new GuardedInvocation(methodHandle,
-                        find.isInherited()? getMap().getProtoGetSwitchPoint(NO_SUCH_PROPERTY_NAME) : null,
+                        find.isInherited()? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
                         getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class), find.getOwner(), func));
             }
         }
@@ -1995,7 +2001,7 @@
     }
 
     private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
-        return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(name), NashornGuards.getMapGuard(getMap()));
+        return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), getMap().getProtoGetSwitchPoint(proto, name), NashornGuards.getMapGuard(getMap()));
     }
 
     private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
@@ -2070,36 +2076,39 @@
      * @return Added property.
      */
     private Property addSpillProperty(final String key, final int propertyFlags) {
-        int i = findEmbed();
-        Property spillProperty;
-
-        if (i >= EMBED_SIZE) {
-            i = getMap().getSpillLength();
+        int fieldCount   = getMap().getFieldCount();
+        int fieldMaximum = getMap().getFieldMaximum();
+        Property property;
+
+        if (fieldCount < fieldMaximum) {
+            property = new AccessorProperty(key, propertyFlags & ~Property.IS_SPILL, getClass(), fieldCount);
+            notifyPropertyAdded(this, property);
+            property = addOwnProperty(property);
+        } else {
+            int i = getMap().getSpillLength();
             MethodHandle getter = MH.arrayElementGetter(Object[].class);
             MethodHandle setter = MH.arrayElementSetter(Object[].class);
             getter = MH.asType(MH.insertArguments(getter, 1, i), Lookup.GET_OBJECT_TYPE);
             setter = MH.asType(MH.insertArguments(setter, 1, i), Lookup.SET_OBJECT_TYPE);
-            spillProperty = new SpillProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter);
-            notifyPropertyAdded(this, spillProperty);
-            spillProperty = addOwnProperty(spillProperty);
-            i = spillProperty.getSlot();
+            property = new AccessorProperty(key, propertyFlags | Property.IS_SPILL, i, getter, setter);
+            notifyPropertyAdded(this, property);
+            property = addOwnProperty(property);
+            i = property.getSlot();
 
             final int newLength = (i + SPILL_RATE) / SPILL_RATE * SPILL_RATE;
-            final Object[] newSpill = new Object[newLength];
-
-            if (spill != null) {
-                System.arraycopy(spill, 0, newSpill, 0, spill.length);
+
+            if (spill == null || newLength > spill.length) {
+                final Object[] newSpill = new Object[newLength];
+
+                if (spill != null) {
+                    System.arraycopy(spill, 0, newSpill, 0, spill.length);
+                }
+
+                spill = newSpill;
             }
-
-            spill = newSpill;
-         } else {
-            useEmbed(i);
-            spillProperty = new SpillProperty(key, propertyFlags, i, GET_EMBED[i], SET_EMBED[i]);
-            notifyPropertyAdded(this, spillProperty);
-            spillProperty = addOwnProperty(spillProperty);
         }
 
-        return spillProperty;
+        return property;
     }
 
 
@@ -3159,67 +3168,22 @@
     }
 
     /*
-     * Embed management
-     */
-
-    /** Number of embed slots */
-    public static final int EMBED_SIZE   = 4;
-    /** Embed offset */
-    public static final int EMBED_OFFSET = 32 - EMBED_SIZE;
-
-    static final MethodHandle[] GET_EMBED;
-    static final MethodHandle[] SET_EMBED;
-
-    static {
-        GET_EMBED = new MethodHandle[EMBED_SIZE];
-        SET_EMBED = new MethodHandle[EMBED_SIZE];
-
-        for (int i = 0; i < EMBED_SIZE; i++) {
-            final String name = "embed" + i;
-            GET_EMBED[i] = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, name, Object.class), Lookup.GET_OBJECT_TYPE);
-            SET_EMBED[i] = MH.asType(MH.setter(MethodHandles.lookup(), ScriptObject.class, name, Object.class), Lookup.SET_OBJECT_TYPE);
-        }
-    }
-
-    void useEmbed(final int i) {
-        flags |= 1 << (EMBED_OFFSET + i);
-    }
-
-    int findEmbed() {
-        final int bits  = ~(flags >>> EMBED_OFFSET);
-        final int least = bits ^ -bits;
-        final int index = Integer.numberOfTrailingZeros(least) - 1;
-
-        return index;
-    }
-
-    /*
      * Make a new UserAccessorProperty property. getter and setter functions are stored in
      * this ScriptObject and slot values are used in property object.
      */
     private UserAccessorProperty newUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
         int oldSpillLength = getMap().getSpillLength();
 
-        int getterSlot = findEmbed();
-        if (getterSlot >= EMBED_SIZE) {
-            getterSlot = oldSpillLength + EMBED_SIZE;
-            ++oldSpillLength;
-        } else {
-            useEmbed(getterSlot);
-        }
-        setEmbedOrSpill(getterSlot, getter);
+        int getterSlot = oldSpillLength++;
+        setSpill(getterSlot, getter);
         // if getter function is null, flag the slot to be negative (less by 1)
         if (getter == null) {
             getterSlot = -getterSlot - 1;
         }
 
-        int setterSlot = findEmbed();
-        if (setterSlot >= EMBED_SIZE) {
-            setterSlot = oldSpillLength + EMBED_SIZE;
-        } else {
-            useEmbed(setterSlot);
-        }
-        setEmbedOrSpill(setterSlot, setter);
+        int setterSlot = oldSpillLength++;
+
+        setSpill(setterSlot, setter);
         // if setter function is null, flag the slot to be negative (less by 1)
         if (setter == null) {
             setterSlot = -setterSlot - 1;
@@ -3228,56 +3192,28 @@
         return new UserAccessorProperty(key, propertyFlags, getterSlot, setterSlot);
     }
 
-    private void setEmbedOrSpill(final int slot, final Object value) {
-        switch (slot) {
-        case 0:
-            embed0 = value;
-            break;
-        case 1:
-            embed1 = value;
-            break;
-        case 2:
-            embed2 = value;
-            break;
-        case 3:
-            embed3 = value;
-            break;
-        default:
-            if (slot >= 0) {
-                final int index = (slot - EMBED_SIZE);
-                if (spill == null) {
-                    // create new spill.
-                    spill = new Object[Math.max(index + 1, SPILL_RATE)];
-                } else if (index >= spill.length) {
-                    // grow spill as needed
-                    final Object[] newSpill = new Object[index + 1];
-                    System.arraycopy(spill, 0, newSpill, 0, spill.length);
-                    spill = newSpill;
-                }
-
-                spill[index] = value;
+    private void setSpill(final int slot, final Object value) {
+        if (slot >= 0) {
+            final int index = slot;
+            if (spill == null) {
+                // create new spill.
+                spill = new Object[Math.max(index + 1, SPILL_RATE)];
+            } else if (index >= spill.length) {
+                // grow spill as needed
+                final Object[] newSpill = new Object[index + 1];
+                System.arraycopy(spill, 0, newSpill, 0, spill.length);
+                spill = newSpill;
             }
-            break;
+
+            spill[index] = value;
         }
     }
 
-    // user accessors are either stored in embed fields or spill array slots
-    // get the accessor value using slot number. Note that slot is either embed
-    // field number or (spill array index + embedSize).
-    Object getEmbedOrSpill(final int slot) {
-        switch (slot) {
-        case 0:
-            return embed0;
-        case 1:
-            return embed1;
-        case 2:
-            return embed2;
-        case 3:
-            return embed3;
-        default:
-            final int index = (slot - EMBED_SIZE);
-            return (index < 0 || (index >= spill.length)) ? null : spill[index];
-        }
+    // user accessors are either stored in spill array slots
+    // get the accessor value using slot number. Note that slot is spill array index.
+    Object getSpill(final int slot) {
+        final int index = slot;
+        return (index < 0 || (index >= spill.length)) ? null : spill[index];
     }
 
     // User defined getter and setter are always called by "dyn:call". Note that the user
@@ -3287,7 +3223,7 @@
     @SuppressWarnings("unused")
     private static Object userAccessorGetter(final ScriptObject proto, final int slot, final Object self) {
         final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
-        final Object       func      = container.getEmbedOrSpill(slot);
+        final Object       func      = container.getSpill(slot);
 
         if (func instanceof ScriptFunction) {
             try {
@@ -3305,7 +3241,7 @@
     @SuppressWarnings("unused")
     private static void userAccessorSetter(final ScriptObject proto, final int slot, final String name, final Object self, final Object value) {
         final ScriptObject container = (proto != null) ? proto : (ScriptObject)self;
-        final Object       func      = container.getEmbedOrSpill(slot);
+        final Object       func      = container.getSpill(slot);
 
         if (func instanceof ScriptFunction) {
             try {
--- a/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/SetMethodCreator.java	Tue Apr 30 10:05:42 2013 -0300
@@ -166,18 +166,20 @@
     }
 
     private SetMethod createNewPropertySetter() {
-        final int nextEmbed = sobj.findEmbed();
-        final SetMethod sm;
-        if (nextEmbed >= ScriptObject.EMBED_SIZE) {
-            sm = createNewSpillPropertySetter();
-        } else {
-            sm = createNewEmbedPropertySetter(nextEmbed);
-        }
-
+        final SetMethod sm = map.getFieldCount() < map.getFieldMaximum() ? createNewFieldSetter() : createNewSpillPropertySetter();
         sobj.notifyPropertyAdded(sobj, sm.property);
         return sm;
     }
 
+    private SetMethod createNewFieldSetter() {
+        final PropertyMap oldMap = getMap();
+        final Property property = new AccessorProperty(getName(), 0, sobj.getClass(), oldMap.getFieldCount());
+        final PropertyMap newMap = oldMap.addProperty(property);
+        MethodHandle setter = MH.insertArguments(ScriptObject.SETFIELD, 0, desc, oldMap, newMap, property.getSetter(Object.class, newMap));
+
+        return new SetMethod(MH.asType(setter, Lookup.SET_OBJECT_TYPE), property);
+    }
+
     private SetMethod createNewSpillPropertySetter() {
         final int nextSpill = getMap().getSpillLength();
 
@@ -189,7 +191,7 @@
         final MethodHandle getter = MH.asType(MH.insertArguments(MH.arrayElementGetter(Object[].class), 1, nextSpill), Lookup.GET_OBJECT_TYPE);
         final MethodHandle setter = MH.asType(MH.insertArguments(MH.arrayElementSetter(Object[].class), 1, nextSpill), Lookup.SET_OBJECT_TYPE);
 
-        return new SpillProperty(getName(), Property.IS_SPILL, nextSpill, getter, setter);
+        return new AccessorProperty(getName(), Property.IS_SPILL, nextSpill, getter, setter);
     }
 
     private MethodHandle createSpillMethodHandle(final int nextSpill, Property property) {
@@ -207,14 +209,6 @@
         }
     }
 
-    private SetMethod createNewEmbedPropertySetter(final int nextEmbed) {
-        sobj.useEmbed(nextEmbed);
-        final Property property = new SpillProperty(getName(), 0, nextEmbed, ScriptObject.GET_EMBED[nextEmbed], ScriptObject.SET_EMBED[nextEmbed]);
-        //TODO specfields
-        final MethodHandle methodHandle = MH.insertArguments(ScriptObject.SETEMBED, 0, desc, getMap(), getNewMap(property), property.getSetter(Object.class, getMap()), nextEmbed);
-        return new SetMethod(methodHandle, property);
-    }
-
     private PropertyMap getNewMap(Property property) {
         return getMap().addProperty(property);
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/SpillProperty.java	Tue Apr 30 09:42:13 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +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 static jdk.nashorn.internal.lookup.Lookup.MH;
-
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import jdk.nashorn.internal.lookup.Lookup;
-
-/**
- * The SpillProperty is a subclass of AccessorProperties. Anything not in the initial property map
- * will end up in the embed fields of the ScriptObject or in the Spill, which currently is a growing
- * Object only array in ScriptObject
- *
- * @see AccessorProperty
- * @see ScriptObject
- */
-public final class SpillProperty extends AccessorProperty {
-    private static final MethodHandle SPILLGETTER = MH.asType(MH.getter(MethodHandles.lookup(), ScriptObject.class, "spill", Object[].class), Lookup.GET_OBJECT_TYPE);
-
-    /**
-     * Constructor
-     *
-     * @param key    property key
-     * @param flags  property flags
-     * @param slot   property slot/index
-     * @param getter getter for property
-     * @param setter setter for property, or null if not configurable and writable
-     */
-    public SpillProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
-        super(key, flags, slot, getter, setter);
-    }
-
-    private SpillProperty(final SpillProperty property) {
-        super(property);
-    }
-
-    @Override
-    protected Property copy() {
-        return new SpillProperty(this);
-    }
-
-    @Override
-    public MethodHandle getGetter(final Class<?> type) {
-        if (isSpill()) {
-            return MH.filterArguments(super.getGetter(type), 0, SPILLGETTER);
-        }
-
-        return super.getGetter(type);
-    }
-
-    @Override
-    public MethodHandle getSetter(final Class<?> type, final PropertyMap currentMap) {
-        if (isSpill()) {
-            return MH.filterArguments(super.getSetter(type, currentMap), 0, SPILLGETTER);
-        }
-
-        return super.getSetter(type, currentMap);
-    }
-
-}
--- a/nashorn/src/jdk/nashorn/internal/runtime/StructureLoader.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/StructureLoader.java	Tue Apr 30 10:05:42 2013 -0300
@@ -110,8 +110,7 @@
     @Override
     protected Class<?> findClass(final String name) throws ClassNotFoundException {
         if (name.startsWith(JS_OBJECT_PREFIX_EXTERNAL)) {
-            final int start = name.indexOf(JS_OBJECT_PREFIX.symbolName()) + JS_OBJECT_PREFIX.symbolName().length();
-            return generateClass(name, name.substring(start, name.length()));
+            return generateClass(name, name.substring(JS_OBJECT_PREFIX_EXTERNAL.length()));
         }
         return super.findClass(name);
     }
--- a/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java	Tue Apr 30 10:05:42 2013 -0300
@@ -67,7 +67,6 @@
 
     private UserAccessorProperty(final UserAccessorProperty property) {
         super(property);
-
         this.getterSlot = property.getterSlot;
         this.setterSlot = property.setterSlot;
     }
@@ -115,10 +114,10 @@
     public int getSpillCount() {
         // calculate how many spill array slots used by this propery.
         int count = 0;
-        if (getGetterSlot() >= ScriptObject.EMBED_SIZE) {
+        if (getGetterSlot() >= 0) {
             count++;
         }
-        if (getSetterSlot() >= ScriptObject.EMBED_SIZE) {
+        if (getSetterSlot() >= 0) {
             count++;
         }
         return count;
@@ -141,7 +140,7 @@
 
     @Override
     public ScriptFunction getGetterFunction(final ScriptObject obj) {
-        final Object value = obj.getEmbedOrSpill(getterSlot);
+        final Object value = obj.getSpill(getterSlot);
         return (value instanceof ScriptFunction) ? (ScriptFunction) value : null;
     }
 
@@ -152,7 +151,7 @@
 
     @Override
     public ScriptFunction getSetterFunction(final ScriptObject obj) {
-        final Object value = obj.getEmbedOrSpill(setterSlot);
+        final Object value = obj.getSpill(setterSlot);
         return (value instanceof ScriptFunction) ? (ScriptFunction) value : null;
     }
 
--- a/nashorn/src/jdk/nashorn/internal/scripts/JO.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/internal/scripts/JO.java	Tue Apr 30 10:05:42 2013 -0300
@@ -32,12 +32,11 @@
  * Empty object class.
  */
 public class JO extends ScriptObject {
-
     /**
      * Constructor
      */
     public JO() {
-        super();
+        super(PropertyMap.newMap(JO.class));
     }
 
     /**
--- a/nashorn/src/jdk/nashorn/tools/Shell.java	Tue Apr 30 09:42:13 2013 +0200
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java	Tue Apr 30 10:05:42 2013 -0300
@@ -343,7 +343,7 @@
      * @return error code
      * @throws IOException when any script file read results in I/O error
      */
-    private int runFXScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
+    private static int runFXScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
         final ScriptObject oldGlobal = Context.getGlobal();
         final boolean globalChanged = (oldGlobal != global);
         try {