changeset 18408:2c3666f44855

Truffle: initial commit of object API implementation
author Andreas Woess <andreas.woess@jku.at>
date Tue, 18 Nov 2014 23:19:43 +0100
parents f439fdb137a3
children d405651001d1
files graal/com.oracle.truffle.object.basic/src/META-INF/services/com.oracle.truffle.api.object.LayoutFactory graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicAllocator.java graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicLayout.java graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicLocations.java graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultLayoutFactory.java graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultStrategy.java graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DynamicObjectBasic.java graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/ShapeBasic.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Debug.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/DebugShapeVisitor.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/DynamicObjectImpl.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutImpl.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LocationImpl.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Locations.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/ObjectStorageOptions.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyImpl.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyMap.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/ShapeImpl.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Transition.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/GraphvizShapeVisitor.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/JSONShapeVisitor.java graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/ShapeProfiler.java mx/suite.py
diffstat 24 files changed, 4938 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/META-INF/services/com.oracle.truffle.api.object.LayoutFactory	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,1 @@
+com.oracle.truffle.object.basic.DefaultLayoutFactory
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicAllocator.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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 com.oracle.truffle.object.basic;
+
+import static com.oracle.truffle.object.basic.BasicLocations.*;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+import com.oracle.truffle.object.LocationImpl.InternalLongLocation;
+import com.oracle.truffle.object.Locations.ConstantLocation;
+import com.oracle.truffle.object.Locations.DeclaredDualLocation;
+import com.oracle.truffle.object.Locations.DualLocation;
+import com.oracle.truffle.object.Locations.ValueLocation;
+import com.oracle.truffle.object.basic.BasicLocations.BooleanLocationDecorator;
+import com.oracle.truffle.object.basic.BasicLocations.DoubleLocationDecorator;
+import com.oracle.truffle.object.basic.BasicLocations.IntLocationDecorator;
+import com.oracle.truffle.object.basic.BasicLocations.LongArrayLocation;
+import com.oracle.truffle.object.basic.BasicLocations.LongFieldLocation;
+import com.oracle.truffle.object.basic.BasicLocations.ObjectArrayLocation;
+
+public abstract class BasicAllocator extends ShapeImpl.BaseAllocator {
+
+    public BasicAllocator(LayoutImpl layout) {
+        super(layout);
+        advance(((BasicLayout) layout).getPrimitiveArrayLocation());
+    }
+
+    public BasicAllocator(ShapeImpl shape) {
+        super(shape);
+    }
+
+    private BasicLayout getLayout() {
+        return (BasicLayout) layout;
+    }
+
+    @Override
+    protected Location moveLocation(Location oldLocation) {
+        if (oldLocation instanceof DeclaredDualLocation) {
+            return advance(newDeclaredDualLocation(((DeclaredDualLocation) oldLocation).get(null, false)));
+        } else if (oldLocation instanceof DualLocation) {
+            return advance(newDualLocation(((DualLocation) oldLocation).getType()));
+        } else if (oldLocation instanceof LongLocation) {
+            return newLongLocation(oldLocation.isFinal());
+        } else if (oldLocation instanceof IntLocation) {
+            return newIntLocation(oldLocation.isFinal());
+        } else if (oldLocation instanceof DoubleLocation) {
+            return newDoubleLocation(oldLocation.isFinal());
+        } else if (oldLocation instanceof BooleanLocation) {
+            return newBooleanLocation(oldLocation.isFinal());
+        } else if (oldLocation instanceof ObjectLocation) {
+            return newObjectLocation(oldLocation.isFinal(), ((ObjectLocation) oldLocation).isNonNull());
+        } else {
+            assert oldLocation instanceof ValueLocation;
+            return advance(oldLocation);
+        }
+    }
+
+    @Override
+    public Location newObjectLocation(boolean useFinal, boolean nonNull) {
+        if (ObjectStorageOptions.InObjectFields) {
+            int insertPos = objectFieldSize;
+            while (insertPos + OBJECT_SIZE <= getLayout().getObjectFieldCount()) {
+                return advance((Location) getLayout().getObjectFieldLocation(insertPos));
+            }
+        }
+        return newObjectArrayLocation(useFinal, nonNull);
+    }
+
+    @SuppressWarnings("unused")
+    private Location newObjectArrayLocation(boolean useFinal, boolean nonNull) {
+        return advance(new ObjectArrayLocation(objectArraySize, getLayout().getObjectArrayLocation()));
+    }
+
+    @Override
+    public Location newTypedObjectLocation(boolean useFinal, Class<?> type, boolean nonNull) {
+        return newObjectLocation(useFinal, nonNull);
+    }
+
+    @Override
+    public Location newIntLocation(boolean useFinal) {
+        if (ObjectStorageOptions.PrimitiveLocations && ObjectStorageOptions.IntegerLocations) {
+            if (ObjectStorageOptions.InObjectFields && primitiveFieldSize + LONG_SIZE <= getLayout().getPrimitiveFieldCount()) {
+                return advance(new IntLocationDecorator(getLayout().getPrimitiveFieldLocation(primitiveFieldSize)));
+            } else if (getLayout().hasPrimitiveExtensionArray() && isPrimitiveExtensionArrayAvailable()) {
+                return advance(new IntLocationDecorator(new LongArrayLocation(primitiveArraySize, getLayout().getPrimitiveArrayLocation())));
+            }
+        }
+        return newObjectLocation(useFinal, true);
+    }
+
+    @Override
+    public Location newDoubleLocation(boolean useFinal) {
+        if (ObjectStorageOptions.PrimitiveLocations && ObjectStorageOptions.DoubleLocations) {
+            if (ObjectStorageOptions.InObjectFields && primitiveFieldSize + LONG_SIZE <= getLayout().getPrimitiveFieldCount()) {
+                return advance(new DoubleLocationDecorator(getLayout().getPrimitiveFieldLocation(primitiveFieldSize), getLayout().isAllowedIntToDouble()));
+            } else if (getLayout().hasPrimitiveExtensionArray() && isPrimitiveExtensionArrayAvailable()) {
+                return advance(new DoubleLocationDecorator(new LongArrayLocation(primitiveArraySize, getLayout().getPrimitiveArrayLocation()), getLayout().isAllowedIntToDouble()));
+            }
+        }
+        return newObjectLocation(useFinal, true);
+    }
+
+    @Override
+    public Location newLongLocation(boolean useFinal) {
+        if (ObjectStorageOptions.PrimitiveLocations && ObjectStorageOptions.LongLocations) {
+            if (ObjectStorageOptions.InObjectFields && primitiveFieldSize + LONG_SIZE <= getLayout().getPrimitiveFieldCount()) {
+                return advance((Location) LongFieldLocation.create(getLayout().getPrimitiveFieldLocation(primitiveFieldSize), getLayout().isAllowedIntToLong()));
+            } else if (getLayout().hasPrimitiveExtensionArray() && isPrimitiveExtensionArrayAvailable()) {
+                return advance(new LongArrayLocation(primitiveArraySize, getLayout().getPrimitiveArrayLocation(), getLayout().isAllowedIntToLong()));
+            }
+        }
+        return newObjectLocation(useFinal, true);
+    }
+
+    @Override
+    public Location newBooleanLocation(boolean useFinal) {
+        if (ObjectStorageOptions.PrimitiveLocations && ObjectStorageOptions.BooleanLocations) {
+            if (primitiveFieldSize + LONG_SIZE <= getLayout().getPrimitiveFieldCount()) {
+                return advance(new BooleanLocationDecorator(getLayout().getPrimitiveFieldLocation(primitiveFieldSize)));
+            }
+        }
+        return newObjectLocation(useFinal, true);
+    }
+
+    private boolean isPrimitiveExtensionArrayAvailable() {
+        return hasPrimitiveArray;
+    }
+
+    @Override
+    protected Location locationForValueUpcast(Object value, Location oldLocation) {
+        assert !(value instanceof Class);
+        if (oldLocation instanceof DualLocation) {
+            DualLocation dualLocation = (DualLocation) oldLocation;
+            if (dualLocation.getType() == null) {
+                if (value instanceof Integer) {
+                    return dualLocation.changeType(int.class);
+                } else if (value instanceof Double) {
+                    return dualLocation.changeType(double.class);
+                } else if (value instanceof Long) {
+                    return dualLocation.changeType(long.class);
+                } else if (value instanceof Boolean) {
+                    return dualLocation.changeType(boolean.class);
+                } else {
+                    return dualLocation.changeType(Object.class);
+                }
+            } else if (dualLocation.getType().isPrimitive()) {
+                return dualLocation.changeType(Object.class);
+            } else {
+                throw new UnsupportedOperationException();
+            }
+        } else if (oldLocation instanceof ConstantLocation) {
+            return constantLocation(value);
+        } else {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    @Override
+    protected DeclaredDualLocation newDeclaredDualLocation(Object value) {
+        return new DeclaredDualLocation((InternalLongLocation) newLongLocation(false), (ObjectLocation) newObjectLocation(false, false), value, layout);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicLayout.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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 com.oracle.truffle.object.basic;
+
+import java.util.*;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.api.object.Shape.Allocator;
+import com.oracle.truffle.object.*;
+import com.oracle.truffle.object.LocationImpl.InternalLongLocation;
+import com.oracle.truffle.object.Locations.DualLocation;
+import com.oracle.truffle.object.basic.BasicLocations.ObjectFieldLocation;
+import com.oracle.truffle.object.basic.BasicLocations.SimpleObjectFieldLocation;
+
+public class BasicLayout extends LayoutImpl {
+    private final ObjectLocation[] objectFields;
+    private final InternalLongLocation[] primitiveFields;
+    private final Location objectArrayLocation;
+    private final Location primitiveArrayLocation;
+
+    BasicLayout(EnumSet<ImplicitCast> allowedImplicitCasts, LayoutStrategy strategy) {
+        super(allowedImplicitCasts, DynamicObjectBasic.class, strategy);
+        this.objectFields = DynamicObjectBasic.OBJECT_FIELD_LOCATIONS;
+        this.primitiveFields = DynamicObjectBasic.PRIMITIVE_FIELD_LOCATIONS;
+        this.primitiveArrayLocation = DynamicObjectBasic.PRIMITIVE_ARRAY_LOCATION;
+        this.objectArrayLocation = DynamicObjectBasic.OBJECT_ARRAY_LOCATION;
+    }
+
+    static LayoutImpl createLayoutImpl(EnumSet<ImplicitCast> allowedImplicitCasts, LayoutStrategy strategy) {
+        return new BasicLayout(allowedImplicitCasts, strategy);
+    }
+
+    @Override
+    public DynamicObject newInstance(Shape shape) {
+        return new DynamicObjectBasic(shape);
+    }
+
+    @Override
+    public Shape createShape(ObjectType operations, Object sharedData, int id) {
+        return new ShapeBasic(this, sharedData, operations, id);
+    }
+
+    @Override
+    protected boolean hasObjectExtensionArray() {
+        return true;
+    }
+
+    @Override
+    protected boolean hasPrimitiveExtensionArray() {
+        return true;
+    }
+
+    @Override
+    protected int getObjectFieldCount() {
+        return objectFields.length;
+    }
+
+    @Override
+    protected int getPrimitiveFieldCount() {
+        return primitiveFields.length;
+    }
+
+    @Override
+    protected Location getObjectArrayLocation() {
+        return objectArrayLocation;
+    }
+
+    @Override
+    protected Location getPrimitiveArrayLocation() {
+        return primitiveArrayLocation;
+    }
+
+    protected ObjectLocation getObjectFieldLocation(int index) {
+        return objectFields[index];
+    }
+
+    protected InternalLongLocation getPrimitiveFieldLocation(int index) {
+        return primitiveFields[index];
+    }
+
+    @Override
+    public Allocator createAllocator() {
+        LayoutImpl layout = this;
+        Allocator allocator = getStrategy().createAllocator(layout);
+        return allocator;
+    }
+
+    @Override
+    protected int objectFieldIndex(Location location) {
+        if (location instanceof DualLocation) {
+            return objectFieldIndex((Location) ((DualLocation) location).getObjectLocation());
+        } else if (location instanceof ObjectFieldLocation) {
+            return ((ObjectFieldLocation) location).getIndex();
+        } else if (location instanceof SimpleObjectFieldLocation) {
+            return ((SimpleObjectFieldLocation) location).getIndex();
+        } else {
+            return 0;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicLocations.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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 com.oracle.truffle.object.basic;
+
+import java.lang.invoke.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+import com.oracle.truffle.object.LocationImpl.InternalLongLocation;
+
+/**
+ * Property location.
+ *
+ * @see Shape
+ * @see Property
+ * @see DynamicObject
+ */
+public abstract class BasicLocations {
+    static final int LONG_SIZE = 1;
+    static final int OBJECT_SIZE = 1;
+
+    public abstract static class ArrayLocation extends LocationImpl {
+        protected final int index;
+        protected final Location arrayLocation;
+
+        public ArrayLocation(int index, Location arrayLocation) {
+            this.index = index;
+            this.arrayLocation = arrayLocation;
+        }
+
+        protected final Object getArray(DynamicObject store, boolean condition) {
+            // non-null cast
+            return arrayLocation.get(store, condition);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + index;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            ArrayLocation other = (ArrayLocation) obj;
+            if (index != other.index) {
+                return false;
+            }
+            return true;
+        }
+
+        public final int getIndex() {
+            return index;
+        }
+
+        @Override
+        protected String getWhereString() {
+            return "[" + index + "]";
+        }
+    }
+
+    public abstract static class FieldLocation extends LocationImpl {
+        private final int index;
+
+        public FieldLocation(int index) {
+            this.index = index;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + index;
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            FieldLocation other = (FieldLocation) obj;
+            if (index != other.index) {
+                return false;
+            }
+            return true;
+        }
+
+        public final int getIndex() {
+            return index;
+        }
+
+        @Override
+        protected String getWhereString() {
+            return "@" + index;
+        }
+    }
+
+    public abstract static class MethodHandleFieldLocation extends FieldLocation {
+        protected final MethodHandle getter;
+        protected final MethodHandle setter;
+
+        public MethodHandleFieldLocation(int index, MethodHandle getter, MethodHandle setter) {
+            super(index);
+            this.getter = getter;
+            this.setter = setter;
+        }
+    }
+
+    public static class ObjectArrayLocation extends ArrayLocation implements ObjectLocation {
+        public ObjectArrayLocation(int index, Location arrayLocation) {
+            super(index, arrayLocation);
+        }
+
+        @Override
+        public Object get(DynamicObject store, boolean condition) {
+            return ((Object[]) getArray(store, condition))[index];
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            ((Object[]) getArray(store, false))[index] = value;
+        }
+
+        @Override
+        public boolean canStore(Object value) {
+            return true;
+        }
+
+        public Class<? extends Object> getType() {
+            return Object.class;
+        }
+
+        public final boolean isNonNull() {
+            return false;
+        }
+
+        @Override
+        public int objectArrayCount() {
+            return OBJECT_SIZE;
+        }
+    }
+
+    public static class ObjectFieldLocation extends MethodHandleFieldLocation implements ObjectLocation {
+
+        public ObjectFieldLocation(int index, MethodHandle getter, MethodHandle setter) {
+            super(index, getter, setter);
+        }
+
+        @Override
+        public Object get(DynamicObject store, boolean condition) {
+            try {
+                return getter.invokeExact(store);
+            } catch (Throwable e) {
+                CompilerDirectives.transferToInterpreter();
+                throw new IllegalStateException(e);
+            }
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            try {
+                setter.invokeExact(store, value);
+            } catch (Throwable e) {
+                CompilerDirectives.transferToInterpreter();
+                throw new IllegalStateException(e);
+            }
+        }
+
+        @Override
+        public boolean canStore(Object value) {
+            return true;
+        }
+
+        public Class<? extends Object> getType() {
+            return Object.class;
+        }
+
+        public boolean isNonNull() {
+            return false;
+        }
+
+        @Override
+        public int objectFieldCount() {
+            return OBJECT_SIZE;
+        }
+    }
+
+    public abstract static class SimpleObjectFieldLocation extends FieldLocation implements ObjectLocation {
+
+        public SimpleObjectFieldLocation(int index) {
+            super(index);
+        }
+
+        @Override
+        public abstract Object get(DynamicObject store, boolean condition);
+
+        @Override
+        public abstract void setInternal(DynamicObject store, Object value);
+
+        @Override
+        public boolean canStore(Object value) {
+            return true;
+        }
+
+        public Class<? extends Object> getType() {
+            return Object.class;
+        }
+
+        public boolean isNonNull() {
+            return false;
+        }
+
+        @Override
+        public int objectFieldCount() {
+            return OBJECT_SIZE;
+        }
+    }
+
+    public static class LongArrayLocation extends ArrayLocation implements InternalLongLocation {
+        protected final boolean allowInt;
+
+        public LongArrayLocation(int index, Location arrayLocation, boolean allowInt) {
+            super(index, arrayLocation);
+            this.allowInt = allowInt;
+        }
+
+        public LongArrayLocation(int index, Location arrayLocation) {
+            this(index, arrayLocation, false);
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return getLong(store, condition);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (canStore(value)) {
+                setLongInternal(store, ((Number) value).longValue());
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        @Override
+        public long getLong(DynamicObject store, boolean condition) {
+            return ((long[]) getArray(store, condition))[index];
+        }
+
+        public final void setLongInternal(DynamicObject store, long value) {
+            ((long[]) getArray(store, false))[index] = value;
+        }
+
+        @Override
+        public void setLong(DynamicObject store, long value, Shape shape) throws FinalLocationException {
+            setLongInternal(store, value);
+        }
+
+        @Override
+        public final void setLong(DynamicObject store, long value, Shape oldShape, Shape newShape) {
+            store.setShapeAndGrow(oldShape, newShape);
+            setLongInternal(store, value);
+        }
+
+        @Override
+        public final void setLong(DynamicObject store, long value) throws FinalLocationException {
+            setLong(store, value, null);
+        }
+
+        public final long getLong(DynamicObject store, Shape shape) {
+            return getLong(store, checkShape(store, shape));
+        }
+
+        @Override
+        public final boolean canStore(Object value) {
+            return value instanceof Long || (allowInt && value instanceof Integer);
+        }
+
+        public Class<Long> getType() {
+            return long.class;
+        }
+
+        @Override
+        public int primitiveArrayCount() {
+            return LONG_SIZE;
+        }
+    }
+
+    public static class LongFieldLocation extends MethodHandleFieldLocation implements InternalLongLocation {
+        public LongFieldLocation(int index, MethodHandle getter, MethodHandle setter) {
+            super(index, getter, setter);
+        }
+
+        public static LongLocation create(InternalLongLocation longLocation, boolean allowInt) {
+            if ((!allowInt && (longLocation instanceof LongLocationDecorator)) || (longLocation instanceof LongLocationDecorator && ((LongLocationDecorator) longLocation).allowInt == allowInt)) {
+                return longLocation;
+            } else {
+                return new LongLocationDecorator(longLocation, allowInt);
+            }
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return getLong(store, condition);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (canStore(value)) {
+                setLongInternal(store, (long) value);
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        @Override
+        public final boolean canStore(Object value) {
+            return value instanceof Long;
+        }
+
+        @Override
+        public final void setLong(DynamicObject store, long value, Shape oldShape, Shape newShape) {
+            store.setShapeAndGrow(oldShape, newShape);
+            setLongInternal(store, value);
+        }
+
+        public long getLong(DynamicObject store, boolean condition) {
+            try {
+                return (long) getter.invokeExact(store);
+            } catch (Throwable e) {
+                CompilerDirectives.transferToInterpreter();
+                throw new IllegalStateException(e);
+            }
+        }
+
+        public void setLong(DynamicObject store, long value, Shape shape) {
+            setLongInternal(store, value);
+        }
+
+        public final void setLong(DynamicObject store, long value) throws FinalLocationException {
+            setLong(store, value, null);
+        }
+
+        public final void setLongInternal(DynamicObject store, long value) {
+            try {
+                setter.invokeExact(store, value);
+            } catch (Throwable e) {
+                CompilerDirectives.transferToInterpreter();
+                throw new IllegalStateException(e);
+            }
+        }
+
+        public final long getLong(DynamicObject store, Shape shape) {
+            return getLong(store, checkShape(store, shape));
+        }
+
+        @Override
+        public final int primitiveFieldCount() {
+            return LONG_SIZE;
+        }
+
+        public Class<Long> getType() {
+            return long.class;
+        }
+    }
+
+    public static class LongLocationDecorator extends PrimitiveLocationDecorator implements InternalLongLocation {
+        protected final boolean allowInt;
+
+        public LongLocationDecorator(InternalLongLocation longLocation, boolean allowInt) {
+            super(longLocation);
+            this.allowInt = allowInt;
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return getLong(store, condition);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (canStore(value)) {
+                setLongInternal(store, ((Number) value).longValue());
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        @Override
+        public final boolean canStore(Object value) {
+            return value instanceof Long || (allowInt && value instanceof Integer);
+        }
+
+        @Override
+        public final void setLong(DynamicObject store, long value, Shape oldShape, Shape newShape) {
+            store.setShapeAndGrow(oldShape, newShape);
+            setLongInternal(store, value);
+        }
+
+        public Class<Long> getType() {
+            return long.class;
+        }
+    }
+
+    public abstract static class SimpleLongFieldLocation extends FieldLocation implements InternalLongLocation {
+
+        public SimpleLongFieldLocation(int index) {
+            super(index);
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return getLong(store, condition);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (canStore(value)) {
+                setLongInternal(store, ((Number) value).longValue());
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        @Override
+        public final boolean canStore(Object value) {
+            return value instanceof Long;
+        }
+
+        @Override
+        public final void setLong(DynamicObject store, long value, Shape oldShape, Shape newShape) {
+            store.setShapeAndGrow(oldShape, newShape);
+            setLongInternal(store, value);
+        }
+
+        public abstract long getLong(DynamicObject store, boolean condition);
+
+        public final long getLong(DynamicObject store, Shape shape) {
+            return getLong(store, checkShape(store, shape));
+        }
+
+        public final void setLong(DynamicObject store, long value) {
+            setLong(store, value, null);
+        }
+
+        public void setLong(DynamicObject store, long value, Shape shape) {
+            setLongInternal(store, value);
+        }
+
+        public abstract void setLongInternal(DynamicObject store, long value);
+
+        @Override
+        public final int primitiveFieldCount() {
+            return LONG_SIZE;
+        }
+
+        public Class<Long> getType() {
+            return long.class;
+        }
+    }
+
+    public abstract static class PrimitiveLocationDecorator extends LocationImpl {
+        private final InternalLongLocation longLocation;
+
+        public PrimitiveLocationDecorator(InternalLongLocation longLocation) {
+            this.longLocation = longLocation;
+        }
+
+        public final long getLong(DynamicObject store, Shape shape) {
+            return longLocation.getLong(store, shape);
+        }
+
+        public final long getLong(DynamicObject store, boolean condition) {
+            return longLocation.getLong(store, condition);
+        }
+
+        public final void setLong(DynamicObject store, long value, Shape shape) throws FinalLocationException {
+            longLocation.setLong(store, value, shape);
+        }
+
+        public final void setLong(DynamicObject store, long value) throws FinalLocationException {
+            longLocation.setLong(store, value);
+        }
+
+        public final void setLongInternal(DynamicObject store, long value) {
+            longLocation.setLongInternal(store, value);
+        }
+
+        @Override
+        public final int primitiveFieldCount() {
+            return ((LocationImpl) longLocation).primitiveFieldCount();
+        }
+
+        @Override
+        public final int primitiveArrayCount() {
+            return ((LocationImpl) longLocation).primitiveArrayCount();
+        }
+    }
+
+    public static class IntLocationDecorator extends PrimitiveLocationDecorator implements IntLocation {
+        public IntLocationDecorator(InternalLongLocation longLocation) {
+            super(longLocation);
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return getInt(store, condition);
+        }
+
+        public int getInt(DynamicObject store, boolean condition) {
+            return (int) getLong(store, condition);
+        }
+
+        public void setInt(DynamicObject store, int value, Shape shape) throws FinalLocationException {
+            setLong(store, value, shape);
+        }
+
+        @Override
+        public final void setInt(DynamicObject store, int value) throws FinalLocationException {
+            setInt(store, value, null);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (canStore(value)) {
+                setLongInternal(store, (int) value);
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        public final int getInt(DynamicObject store, Shape shape) {
+            return getInt(store, checkShape(store, shape));
+        }
+
+        @Override
+        public final boolean canStore(Object value) {
+            return value instanceof Integer;
+        }
+
+        @Override
+        public final void setInt(DynamicObject store, int value, Shape oldShape, Shape newShape) {
+            store.setShapeAndGrow(oldShape, newShape);
+            setLongInternal(store, value);
+        }
+
+        public Class<Integer> getType() {
+            return int.class;
+        }
+    }
+
+    public static class DoubleLocationDecorator extends PrimitiveLocationDecorator implements DoubleLocation {
+        private final boolean allowInt;
+
+        public DoubleLocationDecorator(InternalLongLocation longLocation, boolean allowInt) {
+            super(longLocation);
+            this.allowInt = allowInt;
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return getDouble(store, condition);
+        }
+
+        public double getDouble(DynamicObject store, boolean condition) {
+            return Double.longBitsToDouble(getLong(store, condition));
+        }
+
+        public void setDouble(DynamicObject store, double value, Shape shape) {
+            setLongInternal(store, Double.doubleToRawLongBits(value));
+        }
+
+        public void setDouble(DynamicObject store, double value) {
+            setDouble(store, value, null);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (canStore(value)) {
+                setDouble(store, ((Number) value).doubleValue(), null);
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        public final double getDouble(DynamicObject store, Shape shape) {
+            return getDouble(store, checkShape(store, shape));
+        }
+
+        @Override
+        public final boolean canStore(Object value) {
+            return value instanceof Double || (allowInt && value instanceof Integer);
+        }
+
+        @Override
+        public final void setDouble(DynamicObject store, double value, Shape oldShape, Shape newShape) {
+            store.setShapeAndGrow(oldShape, newShape);
+            setDouble(store, value, newShape);
+        }
+
+        public Class<Double> getType() {
+            return double.class;
+        }
+    }
+
+    public static class BooleanLocationDecorator extends PrimitiveLocationDecorator implements BooleanLocation {
+        public BooleanLocationDecorator(InternalLongLocation longLocation) {
+            super(longLocation);
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return getBoolean(store, condition);
+        }
+
+        public boolean getBoolean(DynamicObject store, boolean condition) {
+            return getLong(store, condition) != 0;
+        }
+
+        public void setBoolean(DynamicObject store, boolean value, Shape shape) {
+            setLongInternal(store, value ? 1 : 0);
+        }
+
+        public void setBoolean(DynamicObject store, boolean value) {
+            setBoolean(store, value, null);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (canStore(value)) {
+                setBoolean(store, (boolean) value, null);
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        public final boolean getBoolean(DynamicObject store, Shape shape) {
+            return getBoolean(store, checkShape(store, shape));
+        }
+
+        @Override
+        public final boolean canStore(Object value) {
+            return value instanceof Boolean;
+        }
+
+        @Override
+        public final void setBoolean(DynamicObject store, boolean value, Shape oldShape, Shape newShape) {
+            store.setShapeAndGrow(oldShape, newShape);
+            setBoolean(store, value, newShape);
+        }
+
+        public Class<Boolean> getType() {
+            return boolean.class;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultLayoutFactory.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object.basic;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+
+public class DefaultLayoutFactory implements LayoutFactory {
+    public Layout createLayout(LayoutBuilder layoutBuilder) {
+        return BasicLayout.createLayoutImpl(layoutBuilder.getAllowedImplicitCasts(), new DefaultStrategy());
+    }
+
+    public Property createProperty(Object id, Location location, int flags) {
+        return new PropertyImpl(id, location, flags);
+    }
+
+    public int getPriority() {
+        return 10;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultStrategy.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object.basic;
+
+import java.util.*;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+import com.oracle.truffle.object.ShapeImpl.BaseAllocator;
+
+class DefaultStrategy implements LayoutStrategy {
+    public boolean updateShape(DynamicObject object) {
+        assert object.getShape().isValid();
+        return false;
+    }
+
+    public Shape returnCached(Shape newShape) {
+        assert newShape.isValid();
+        return newShape;
+    }
+
+    private static boolean assertLocationInRange(Shape shape, Location location) {
+        BasicLayout layout = (BasicLayout) shape.getLayout();
+        assert (shape.getPrimitiveFieldSize() + ((LocationImpl) location).primitiveFieldCount() <= layout.getPrimitiveFieldCount());
+        assert (shape.getObjectFieldSize() + ((LocationImpl) location).objectFieldCount() <= layout.getObjectFieldCount());
+        return true;
+    }
+
+    public Shape ensureSpace(Shape shape, Location location) {
+        Objects.requireNonNull(location);
+        assert assertLocationInRange(shape, location);
+        return shape;
+    }
+
+    public boolean isAutoExtArray() {
+        return false;
+    }
+
+    public Property generalizeProperty(DynamicObject object, Property oldProperty, Object value) {
+        Shape oldShape = object.getShape();
+        Location oldLocation = oldProperty.getLocation();
+        Location newLocation = ((BasicAllocator) oldShape.allocator()).locationForValueUpcast(value, oldLocation);
+        Property newProperty = oldProperty.relocate(newLocation);
+        Shape newShape = oldShape.replaceProperty(oldProperty, newProperty);
+        newProperty.setSafe(object, value, oldShape, newShape);
+        return newProperty;
+    }
+
+    public Property generalizeProperty(DynamicObject object, Property oldProperty, Object value, Shape currentShape, Shape oldNewShape) {
+        Location oldLocation = oldProperty.getLocation();
+        Location newLocation = ((BasicAllocator) currentShape.allocator()).locationForValueUpcast(value, oldLocation);
+        Property newProperty = oldProperty.relocate(newLocation);
+        Shape newShape = oldNewShape.replaceProperty(oldProperty, newProperty);
+        newProperty.setSafe(object, value, currentShape, newShape);
+        return newProperty;
+    }
+
+    public BaseAllocator createAllocator(Shape shape) {
+        return new DefaultAllocatorImpl((ShapeImpl) shape);
+    }
+
+    public BaseAllocator createAllocator(Layout layout) {
+        return new DefaultAllocatorImpl((LayoutImpl) layout);
+    }
+
+    public static class DefaultAllocatorImpl extends BasicAllocator {
+        protected DefaultAllocatorImpl(LayoutImpl layout) {
+            super(layout);
+        }
+
+        protected DefaultAllocatorImpl(ShapeImpl shape) {
+            super(shape);
+        }
+
+        @Override
+        public Location locationForValue(Object value, boolean useFinal, boolean nonNull) {
+            return super.newDualLocationForValue(value);
+        }
+
+        @Override
+        public Location declaredLocation(Object value) {
+            return super.newDeclaredDualLocation(value);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DynamicObjectBasic.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 com.oracle.truffle.object.basic;
+
+import java.lang.annotation.*;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+import com.oracle.truffle.object.basic.BasicLocations.*;
+
+public class DynamicObjectBasic extends DynamicObjectImpl {
+    @Retention(RetentionPolicy.RUNTIME)
+    protected @interface DynamicField {
+    }
+
+    @DynamicField private long primitive1;
+    @DynamicField private long primitive2;
+    @DynamicField private long primitive3;
+    @DynamicField private Object object1;
+    @DynamicField private Object object2;
+    @DynamicField private Object object3;
+    @DynamicField private Object object4;
+    private Object[] objext;
+    private long[] primext;
+
+    public DynamicObjectBasic(Shape shape) {
+        super(shape);
+    }
+
+    @Override
+    protected final void initialize(Shape shape) {
+        assert getObjectStore(shape) == null;
+        int capacity = ((ShapeImpl) shape).getObjectArrayCapacity();
+        if (capacity != 0) {
+            this.setObjectStore(new Object[capacity], shape);
+        }
+        if (((ShapeImpl) shape).getPrimitiveArrayCapacity() != 0) {
+            this.setPrimitiveStore(new long[((ShapeImpl) shape).getPrimitiveArrayCapacity()], shape);
+        }
+    }
+
+    /**
+     * Simpler version of {@link #resizeObjectStore} when the object is only increasing in size.
+     */
+    @Override
+    protected final void growObjectStore(Shape oldShape, Shape newShape) {
+        int oldObjectArrayCapacity = ((ShapeImpl) oldShape).getObjectArrayCapacity();
+        int newObjectArrayCapacity = ((ShapeImpl) newShape).getObjectArrayCapacity();
+        if (oldObjectArrayCapacity != newObjectArrayCapacity) {
+            growObjectStoreIntl(oldObjectArrayCapacity, newObjectArrayCapacity, oldShape);
+        }
+    }
+
+    private void growObjectStoreIntl(int oldObjectArrayCapacity, int newObjectArrayCapacity, Shape newShape) {
+        Object[] newObjectStore = new Object[newObjectArrayCapacity];
+        if (oldObjectArrayCapacity != 0) {
+            // monotonic growth assumption
+            assert oldObjectArrayCapacity < newObjectArrayCapacity;
+            Object[] oldObjectStore = this.getObjectStore(newShape);
+            for (int i = 0; i < oldObjectArrayCapacity; ++i) {
+                newObjectStore[i] = oldObjectStore[i];
+            }
+        }
+        this.setObjectStore(newObjectStore, newShape);
+    }
+
+    /**
+     * Simpler version of {@link #resizePrimitiveStore} when the object is only increasing in size.
+     */
+    @Override
+    protected final void growPrimitiveStore(Shape oldShape, Shape newShape) {
+        assert ((ShapeImpl) newShape).hasPrimitiveArray();
+        int oldPrimitiveCapacity = oldShape.getPrimitiveArrayCapacity();
+        int newPrimitiveCapacity = newShape.getPrimitiveArrayCapacity();
+        if (newPrimitiveCapacity == 0) {
+            // due to obsolescence, we might have to reserve an empty primitive array slot
+            this.setPrimitiveStore(null, newShape);
+        } else if (oldPrimitiveCapacity != newPrimitiveCapacity) {
+            growPrimitiveStoreIntl(oldPrimitiveCapacity, newPrimitiveCapacity, oldShape);
+        }
+    }
+
+    private void growPrimitiveStoreIntl(int oldPrimitiveCapacity, int newPrimitiveCapacity, Shape newShape) {
+        long[] newPrimitiveArray = new long[newPrimitiveCapacity];
+        if (oldPrimitiveCapacity != 0) {
+            // primitive array can shrink due to type changes
+            long[] oldPrimitiveArray = this.getPrimitiveStore(newShape);
+            for (int i = 0; i < Math.min(oldPrimitiveCapacity, newPrimitiveCapacity); ++i) {
+                newPrimitiveArray[i] = oldPrimitiveArray[i];
+            }
+        }
+        this.setPrimitiveStore(newPrimitiveArray, newShape);
+    }
+
+    @Override
+    protected final void resizeObjectStore(Shape oldShape, Shape newShape) {
+        Object[] newObjectStore = null;
+        int destinationCapacity = newShape.getObjectArrayCapacity();
+        if (destinationCapacity != 0) {
+            newObjectStore = new Object[destinationCapacity];
+            int sourceCapacity = oldShape.getObjectArrayCapacity();
+            if (sourceCapacity != 0) {
+                Object[] oldObjectStore = getObjectStore(newShape);
+                for (int i = 0; i < Math.min(sourceCapacity, destinationCapacity); ++i) {
+                    newObjectStore[i] = oldObjectStore[i];
+                }
+            }
+        }
+        this.setObjectStore(newObjectStore, newShape);
+    }
+
+    private Object[] getObjectStore(@SuppressWarnings("unused") Shape currentShape) {
+        return objext;
+    }
+
+    private void setObjectStore(Object[] newArray, @SuppressWarnings("unused") Shape currentShape) {
+        objext = newArray;
+    }
+
+    private long[] getPrimitiveStore(@SuppressWarnings("unused") Shape currentShape) {
+        return primext;
+    }
+
+    private void setPrimitiveStore(long[] newArray, @SuppressWarnings("unused") Shape currentShape) {
+        primext = newArray;
+    }
+
+    @Override
+    protected final void resizePrimitiveStore(Shape oldShape, Shape newShape) {
+        assert newShape.hasPrimitiveArray();
+        long[] newPrimitiveArray = null;
+        int destinationCapacity = newShape.getPrimitiveArrayCapacity();
+        if (destinationCapacity != 0) {
+            newPrimitiveArray = new long[destinationCapacity];
+            int sourceCapacity = oldShape.getPrimitiveArrayCapacity();
+            if (sourceCapacity != 0) {
+                long[] oldPrimitiveArray = this.getPrimitiveStore(newShape);
+                for (int i = 0; i < Math.min(sourceCapacity, destinationCapacity); ++i) {
+                    newPrimitiveArray[i] = oldPrimitiveArray[i];
+                }
+            }
+        }
+        this.setPrimitiveStore(newPrimitiveArray, newShape);
+    }
+
+    /**
+     * Check whether fast transition is valid.
+     *
+     * @see #setShapeAndGrow
+     */
+    @SuppressWarnings("unused")
+    private boolean checkSetShape(Shape oldShape, Shape newShape) {
+        Shape currentShape = getShape();
+        assert oldShape != newShape : "Wrong old shape assumption?";
+        assert newShape != currentShape : "Redundant shape change? shape=" + currentShape;
+        assert oldShape == currentShape || oldShape.getParent() == currentShape : "Out-of-order shape change?" + "\nparentShape=" + currentShape + "\noldShape=" + oldShape + "\nnewShape=" + newShape;
+        return true;
+    }
+
+    /**
+     * Check whether the extension arrays are in accordance with the description in the shape.
+     */
+    @Override
+    protected final boolean checkExtensionArrayInvariants(Shape newShape) {
+        assert getShape() == newShape;
+        assert (getObjectStore(newShape) == null && newShape.getObjectArrayCapacity() == 0) ||
+                        (getObjectStore(newShape) != null && getObjectStore(newShape).length == newShape.getObjectArrayCapacity());
+        if (newShape.hasPrimitiveArray()) {
+            assert (getPrimitiveStore(newShape) == null && newShape.getPrimitiveArrayCapacity() == 0) ||
+                            (getPrimitiveStore(newShape) != null && getPrimitiveStore(newShape).length == newShape.getPrimitiveArrayCapacity());
+        }
+        return true;
+    }
+
+    @Override
+    protected final DynamicObject cloneWithShape(Shape currentShape) {
+        assert this.getShape() == currentShape;
+        final DynamicObjectBasic clone = (DynamicObjectBasic) super.clone();
+        if (this.getObjectStore(currentShape) != null) {
+            clone.setObjectStore(this.getObjectStore(currentShape).clone(), currentShape);
+        }
+        if (currentShape.hasPrimitiveArray() && this.getPrimitiveStore(currentShape) != null) {
+            clone.setPrimitiveStore(this.getPrimitiveStore(currentShape).clone(), currentShape);
+        }
+        return clone;
+    }
+
+    protected final void reshape(ShapeImpl newShape) {
+        reshapeCount.inc();
+
+        ShapeImpl oldShape = getShape();
+        ShapeImpl commonAncestor = ShapeImpl.findCommonAncestor(oldShape, newShape);
+        if (ObjectStorageOptions.TraceReshape) {
+            int limit = 150;
+            System.out.printf("RESHAPE\nOLD %s\nNEW %s\nLCA %s\nDIFF %s\n---\n", oldShape.toStringLimit(limit), newShape.toStringLimit(limit), commonAncestor.toStringLimit(limit),
+                            ShapeImpl.diff(oldShape, newShape));
+        }
+
+        DynamicObject original = this.cloneWithShape(oldShape);
+        setShapeAndGrow(oldShape, newShape);
+        assert !((newShape.hasPrimitiveArray() && newShape.getPrimitiveArrayCapacity() == 0)) || getPrimitiveStore(newShape) == null;
+        copyProperties(original, commonAncestor);
+        assert checkExtensionArrayInvariants(newShape);
+    }
+
+    static final SimpleObjectFieldLocation[] OBJECT_FIELD_LOCATIONS;
+    static final SimpleLongFieldLocation[] PRIMITIVE_FIELD_LOCATIONS;
+
+    static final SimpleObjectFieldLocation OBJECT_ARRAY_LOCATION;
+    static final SimpleObjectFieldLocation PRIMITIVE_ARRAY_LOCATION;
+
+    static {
+        int index;
+
+        index = 0;
+        PRIMITIVE_FIELD_LOCATIONS = new SimpleLongFieldLocation[]{new SimpleLongFieldLocation(index++) {
+            @Override
+            public long getLong(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).primitive1;
+            }
+
+            @Override
+            public void setLongInternal(DynamicObject store, long value) {
+                ((DynamicObjectBasic) store).primitive1 = value;
+            }
+        }, new SimpleLongFieldLocation(index++) {
+            @Override
+            public long getLong(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).primitive2;
+            }
+
+            @Override
+            public void setLongInternal(DynamicObject store, long value) {
+                ((DynamicObjectBasic) store).primitive2 = value;
+            }
+        }, new SimpleLongFieldLocation(index++) {
+            @Override
+            public long getLong(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).primitive3;
+            }
+
+            @Override
+            public void setLongInternal(DynamicObject store, long value) {
+                ((DynamicObjectBasic) store).primitive3 = value;
+            }
+        }};
+
+        index = 0;
+        OBJECT_FIELD_LOCATIONS = new SimpleObjectFieldLocation[]{new SimpleObjectFieldLocation(index++) {
+            @Override
+            public Object get(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).object1;
+            }
+
+            @Override
+            public void setInternal(DynamicObject store, Object value) {
+                ((DynamicObjectBasic) store).object1 = value;
+            }
+        }, new SimpleObjectFieldLocation(index++) {
+            @Override
+            public Object get(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).object2;
+            }
+
+            @Override
+            public void setInternal(DynamicObject store, Object value) {
+                ((DynamicObjectBasic) store).object2 = value;
+            }
+        }, new SimpleObjectFieldLocation(index++) {
+            @Override
+            public Object get(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).object3;
+            }
+
+            @Override
+            public void setInternal(DynamicObject store, Object value) {
+                ((DynamicObjectBasic) store).object3 = value;
+            }
+        }, new SimpleObjectFieldLocation(index++) {
+            @Override
+            public Object get(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).object4;
+            }
+
+            @Override
+            public void setInternal(DynamicObject store, Object value) {
+                ((DynamicObjectBasic) store).object4 = value;
+            }
+        }};
+
+        OBJECT_ARRAY_LOCATION = new SimpleObjectFieldLocation(index++) {
+            @Override
+            public Object[] get(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).objext;
+            }
+
+            @Override
+            public void setInternal(DynamicObject store, Object value) {
+                ((DynamicObjectBasic) store).objext = (Object[]) value;
+            }
+        };
+
+        PRIMITIVE_ARRAY_LOCATION = new SimpleObjectFieldLocation(index++) {
+            @Override
+            public long[] get(DynamicObject store, boolean condition) {
+                return ((DynamicObjectBasic) store).primext;
+            }
+
+            @Override
+            public void setInternal(DynamicObject store, Object value) {
+                ((DynamicObjectBasic) store).primext = (long[]) value;
+            }
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/ShapeBasic.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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 com.oracle.truffle.object.basic;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+
+public final class ShapeBasic extends ShapeImpl {
+    public ShapeBasic(Layout layout, Object sharedData, ObjectType operations, int id) {
+        super(layout, operations, sharedData, id);
+    }
+
+    public ShapeBasic(Layout layout, Object sharedData, ShapeImpl parent, ObjectType operations, PropertyMap propertyMap, Allocator allocator, int id) {
+        super(layout, parent, operations, sharedData, propertyMap, allocator, id);
+    }
+
+    @SuppressWarnings("hiding")
+    @Override
+    protected ShapeImpl createShape(Layout layout, Object sharedData, ShapeImpl parent, ObjectType operations, PropertyMap propertyMap, Allocator allocator, int id) {
+        return new ShapeBasic(layout, sharedData, parent, operations, propertyMap, allocator, id);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Debug.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.oracle.truffle.object.debug.*;
+
+class Debug {
+    private static Collection<ShapeImpl> allShapes;
+
+    static void registerShape(ShapeImpl newShape) {
+        allShapes.add(newShape);
+    }
+
+    static {
+        if (ObjectStorageOptions.DumpShapes) {
+            allShapes = new ConcurrentLinkedQueue<>();
+        }
+
+        if (ObjectStorageOptions.DumpShapes) {
+            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                public void run() {
+                    try (PrintWriter out = new PrintWriter("shapes.json", "UTF-8")) {
+                        out.println("{\"shapes\": [");
+                        boolean first = true;
+                        for (ShapeImpl shape : allShapes) {
+                            if (!first) {
+                                out.println(",");
+                            }
+                            first = false;
+                            out.print(shape.accept(new JSONShapeVisitor()));
+                        }
+                        if (!first) {
+                            out.println();
+                        }
+                        out.println("]}");
+                    } catch (FileNotFoundException | UnsupportedEncodingException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/DebugShapeVisitor.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.object.*;
+
+public abstract class DebugShapeVisitor<R> implements ShapeVisitor<R> {
+    public R visitShape(Shape shape) {
+        return visitShape(shape, Collections.unmodifiableMap(((ShapeImpl) shape).getTransitionMapForRead()));
+    }
+
+    public abstract R visitShape(Shape shape, Map<? extends Transition, ? extends Shape> transitions);
+
+    public static String getId(Shape shape) {
+        return Integer.toHexString(shape.hashCode());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/DynamicObjectImpl.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.Locations.ValueLocation;
+import com.oracle.truffle.object.debug.*;
+
+public abstract class DynamicObjectImpl implements DynamicObject, Cloneable {
+    private ShapeImpl shape;
+
+    public static final DebugCounter reshapeCount = DebugCounter.create("Reshape count");
+
+    public DynamicObjectImpl(Shape shape) {
+        assert shape instanceof ShapeImpl;
+        initialize(shape);
+        setShape(shape);
+
+        if (ObjectStorageOptions.Profile) {
+            trackObject(this);
+        }
+    }
+
+    public Object getTypeIdentifier() {
+        return getShape();
+    }
+
+    public ShapeImpl getShape() {
+        return shape;
+    }
+
+    protected void setShape(Shape shape) {
+        assert shape.getLayout().getType().isInstance(this);
+        this.shape = (ShapeImpl) shape;
+    }
+
+    protected abstract void initialize(Shape initialShape);
+
+    public final void setShapeAndResize(Shape newShape) {
+        setShapeAndResize(getShape(), newShape);
+    }
+
+    public final void setShapeAndResize(Shape oldShape, Shape newShape) {
+        assert getShape() == oldShape : "wrong old shape";
+        if (oldShape != newShape) {
+            setShape(newShape);
+            resizeStore(oldShape, newShape);
+
+            assert checkExtensionArrayInvariants(newShape);
+        }
+    }
+
+    /**
+     * Set shape to an immediate child of the current shape, optionally growing the extension array.
+     * Typically this would add a single property. Cannot shrink or grow more than one property at a
+     * time.
+     *
+     * @see #setShapeAndResize(Shape, Shape)
+     */
+    public final void setShapeAndGrow(Shape oldShape, Shape newShape) {
+        assert getShape() == oldShape : "wrong old shape";
+        if (oldShape != newShape) {
+            assert checkSetShape(oldShape, newShape);
+
+            setShape(newShape);
+            growStore(oldShape, newShape);
+
+            assert checkExtensionArrayInvariants(newShape);
+        }
+    }
+
+    /**
+     * Simpler version of {@link #resizeStore} when the object is only increasing in size.
+     */
+    private void growStore(Shape oldShape, Shape newShape) {
+        growObjectStore(oldShape, newShape);
+        if (((ShapeImpl) newShape).hasPrimitiveArray) {
+            growPrimitiveStore(oldShape, newShape);
+        }
+    }
+
+    protected abstract void growObjectStore(Shape oldShape, Shape newShape);
+
+    protected abstract void growPrimitiveStore(Shape oldShape, Shape newShape);
+
+    private void resizeStore(Shape oldShape, Shape newShape) {
+        resizeObjectStore(oldShape, newShape);
+        if (((ShapeImpl) newShape).hasPrimitiveArray) {
+            resizePrimitiveStore(oldShape, newShape);
+        }
+    }
+
+    protected abstract void resizePrimitiveStore(Shape oldShape, Shape newShape);
+
+    protected abstract void resizeObjectStore(Shape oldShape, Shape newShape);
+
+    /**
+     * Check whether fast transition is valid.
+     *
+     * @see #setShapeAndGrow
+     */
+    private boolean checkSetShape(Shape oldShape, Shape newShape) {
+        Shape currentShape = getShape();
+        assert oldShape != newShape : "Wrong old shape assumption?";
+        assert newShape != currentShape : "Redundant shape change? shape=" + currentShape;
+        // assert oldShape == currentShape || (oldShape.getLastProperty() == ((EnterpriseLayout)
+        // oldShape.getLayout()).getPrimitiveArrayProperty() && oldShape.getParent() ==
+        // currentShape) : "Out-of-order shape change?" + "\nparentShape=" + currentShape +
+        // "\noldShape=" + oldShape + "\nnewShape=" + newShape;
+        return true;
+    }
+
+    /**
+     * Check whether the extension arrays are in accordance with the description in the shape.
+     */
+    protected abstract boolean checkExtensionArrayInvariants(Shape newShape);
+
+    @Override
+    protected final DynamicObject clone() {
+        try {
+            return (DynamicObject) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    protected abstract DynamicObject cloneWithShape(Shape currentShape);
+
+    void reshapeAfterDelete(final Shape newShape, final Shape deletedParentShape) {
+        DynamicObject original = this.cloneWithShape(getShape());
+        setShapeAndResize(newShape);
+        copyProperties(original, deletedParentShape);
+    }
+
+    public final void copyProperties(DynamicObject fromObject, Shape ancestor) {
+        Shape fromShape = fromObject.getShape();
+        Shape toShape = getShape();
+        assert toShape.isRelated(ancestor);
+        assert toShape.isValid();
+        assert ancestor.isValid();
+        for (; toShape != ancestor; toShape = toShape.getParent()) {
+            Property toProperty = toShape.getLastProperty();
+            // assumption: hidden properties won't change and don't need copying
+            if (!toProperty.isHidden()) {
+                assert fromShape.hasProperty(toProperty.getKey());
+                Property fromProperty = fromShape.getProperty(toProperty.getKey());
+                // copy only if property has a location and it's not the same as the source location
+                if (toProperty.getLocation() != null && !(toProperty.getLocation() instanceof ValueLocation) && !toProperty.getLocation().equals(fromProperty.getLocation())) {
+                    toProperty.setInternal(this, fromProperty.get(fromObject, false));
+                    assert toShape.isValid();
+                }
+
+                if (fromShape.getLastProperty() == fromProperty) {
+                    // no property is looked up twice, so we can skip over to parent
+                    fromShape = fromShape.getParent();
+                }
+            }
+        }
+    }
+
+    @TruffleBoundary
+    public boolean changeFlags(Object id, int newFlags) {
+        Shape oldShape = getShape();
+        Property existing = oldShape.getProperty(id);
+        if (existing != null) {
+            if (existing.getFlags() != newFlags) {
+                Property newProperty = existing.copyWithFlags(newFlags);
+                Shape newShape = oldShape.replaceProperty(existing, newProperty);
+                this.setShape(newShape);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @TruffleBoundary
+    public boolean changeFlags(Object id, FlagsFunction updateFunction) {
+        Shape oldShape = getShape();
+        Property existing = oldShape.getProperty(id);
+        if (existing != null) {
+            Integer newFlags = updateFunction.apply(existing.getFlags());
+            if (newFlags != null && existing.getFlags() != newFlags.intValue()) {
+                Property newProperty = existing.copyWithFlags(newFlags);
+                Shape newShape = oldShape.replaceProperty(existing, newProperty);
+                this.setShape(newShape);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public String debugDump(int level) {
+        return debugDump(0, level);
+    }
+
+    public String debugDump(int level, int levelStop) {
+        List<Property> properties = this.getShape().getPropertyListInternal(true);
+        StringBuilder sb = new StringBuilder(properties.size() * 10);
+        sb.append("{\n");
+        for (Property property : properties) {
+            indent(sb, level + 1);
+
+            sb.append(property.getKey());
+            sb.append('[').append(property.getLocation()).append(']');
+            Object value = property.get(this, false);
+            if (value instanceof DynamicObjectImpl) {
+                if (level < levelStop) {
+                    value = ((DynamicObjectImpl) value).debugDump(level + 1, levelStop);
+                } else {
+                    value = value.toString();
+                }
+            }
+            sb.append(": ");
+            sb.append(value);
+            if (property != properties.get(properties.size() - 1)) {
+                sb.append(",");
+            }
+            sb.append("\n");
+        }
+        indent(sb, level);
+        sb.append("}");
+        return sb.toString();
+    }
+
+    private static StringBuilder indent(StringBuilder sb, int level) {
+        for (int i = 0; i < level; i++) {
+            sb.append(' ');
+        }
+        return sb;
+    }
+
+    @Override
+    public String toString() {
+        return getShape().getObjectType().toString(this);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return getShape().getObjectType().equals(this, obj);
+    }
+
+    @Override
+    public int hashCode() {
+        return getShape().getObjectType().hashCode(this);
+    }
+
+    @TruffleBoundary
+    public Object get(Object id, Object defaultValue) {
+        Property existing = getShape().getProperty(id);
+        if (existing != null) {
+            return existing.get(this, false);
+        } else {
+            return defaultValue;
+        }
+    }
+
+    @TruffleBoundary
+    public boolean set(Object id, Object value) {
+        Property existing = getShape().getProperty(id);
+        if (existing != null) {
+            existing.setGeneric(this, value, null);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @TruffleBoundary
+    public void define(Object id, Object value, int flags) {
+        ShapeImpl oldShape = getShape();
+        Property existing = oldShape.getProperty(id);
+        if (existing == null) {
+            updateShape();
+            oldShape = getShape();
+            Shape newShape = oldShape.addProperty(Property.create(id, oldShape.allocator().locationForValue(value, true, true), flags));
+            updateShape();
+            newShape.getLastProperty().setGeneric(this, value, oldShape, newShape);
+        } else {
+            defineExisting(id, value, flags, existing, oldShape);
+        }
+    }
+
+    private void defineExisting(Object id, Object value, int flags, Property existing, ShapeImpl oldShape) {
+        if (existing.getFlags() == flags) {
+            existing.setGeneric(this, value, null);
+        } else {
+            Property newProperty = Property.create(id, oldShape.getLayout().existingLocationForValue(value, existing.getLocation(), oldShape), flags);
+            Shape newShape = oldShape.replaceProperty(existing, newProperty);
+            this.setShapeAndResize(newShape);
+            newProperty.setInternal(this, value);
+        }
+    }
+
+    @TruffleBoundary
+    public void define(Object id, Object value, int flags, LocationFactory locationFactory) {
+        ShapeImpl oldShape = getShape();
+        Property existing = oldShape.getProperty(id);
+        if (existing == null) {
+            updateShape();
+            oldShape = getShape();
+            Shape newShape = oldShape.addProperty(Property.create(id, locationFactory.createLocation(oldShape, value), flags));
+            updateShape();
+            newShape.getLastProperty().setGeneric(this, value, oldShape, newShape);
+        } else {
+            defineExisting(id, value, flags, existing, oldShape);
+        }
+    }
+
+    @TruffleBoundary
+    public boolean delete(Object id) {
+        ShapeImpl oldShape = getShape();
+        Property existing = oldShape.getProperty(id);
+        if (existing != null) {
+            ShapeImpl newShape = oldShape.removeProperty(existing);
+            this.reshapeAfterDelete(newShape, ShapeImpl.findCommonAncestor(oldShape, newShape));
+            // TODO ancestor should be the parent of found property's shape
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public int size() {
+        return getShape().getPropertyCount();
+    }
+
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    public final boolean updateShape() {
+        return getShape().getLayout().getStrategy().updateShape(this);
+    }
+
+    private static void trackObject(DynamicObject obj) {
+        ShapeProfiler.getInstance().track(obj);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutImpl.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.api.object.Shape.Allocator;
+import com.oracle.truffle.object.LocationImpl.EffectivelyFinalLocation;
+import com.oracle.truffle.object.LocationImpl.TypedObjectLocation;
+import com.oracle.truffle.object.Locations.ConstantLocation;
+import com.oracle.truffle.object.Locations.DeclaredLocation;
+import com.oracle.truffle.object.Locations.DualLocation;
+import com.oracle.truffle.object.Locations.ValueLocation;
+import com.oracle.truffle.object.ShapeImpl.BaseAllocator;
+
+public abstract class LayoutImpl extends Layout {
+    private static final int INT_TO_DOUBLE_FLAG = 1;
+    private static final int INT_TO_LONG_FLAG = 2;
+
+    private final LayoutStrategy strategy;
+    private final Class<? extends DynamicObject> clazz;
+    private final int allowedImplicitCasts;
+
+    protected LayoutImpl(EnumSet<ImplicitCast> allowedImplicitCasts, Class<? extends DynamicObjectImpl> clazz, LayoutStrategy strategy) {
+        this.strategy = strategy;
+        this.clazz = clazz;
+
+        this.allowedImplicitCasts = (allowedImplicitCasts.contains(ImplicitCast.IntToDouble) ? INT_TO_DOUBLE_FLAG : 0) | (allowedImplicitCasts.contains(ImplicitCast.IntToLong) ? INT_TO_LONG_FLAG : 0);
+    }
+
+    @Override
+    public abstract DynamicObject newInstance(Shape shape);
+
+    @Override
+    public Class<? extends DynamicObject> getType() {
+        return clazz;
+    }
+
+    @Override
+    public final Shape createShape(ObjectType operations, Object sharedData) {
+        return createShape(operations, sharedData, 0);
+    }
+
+    @Override
+    public final Shape createShape(ObjectType operations) {
+        return createShape(operations, null);
+    }
+
+    public boolean isAllowedIntToDouble() {
+        return (allowedImplicitCasts & INT_TO_DOUBLE_FLAG) != 0;
+    }
+
+    public boolean isAllowedIntToLong() {
+        return (allowedImplicitCasts & INT_TO_LONG_FLAG) != 0;
+    }
+
+    protected abstract boolean hasObjectExtensionArray();
+
+    protected abstract boolean hasPrimitiveExtensionArray();
+
+    protected abstract int getObjectFieldCount();
+
+    protected abstract int getPrimitiveFieldCount();
+
+    protected abstract Location getObjectArrayLocation();
+
+    protected abstract Location getPrimitiveArrayLocation();
+
+    protected abstract int objectFieldIndex(Location location);
+
+    protected boolean isLocationAssignableFrom(Location destination, Location source) {
+        LayoutImpl layout = this;
+        if (destination.isFinal()) {
+            // allowed Final<X>Location => Final<X>Location
+            // allowed FinalIntLocation => Final{Int,Double}Location
+            // allowed: Final{Int,Double,TypedObject}Location => FinalObjectLocation
+            if (!source.isFinal()) {
+                return false;
+            }
+        }
+
+        if (destination instanceof IntLocation) {
+            return (source instanceof IntLocation);
+        } else if (destination instanceof DoubleLocation) {
+            return (source instanceof DoubleLocation || (layout.isAllowedIntToDouble() && source instanceof IntLocation));
+        } else if (destination instanceof LongLocation) {
+            return (source instanceof LongLocation || (layout.isAllowedIntToLong() && source instanceof IntLocation));
+        } else if (destination instanceof BooleanLocation) {
+            return (source instanceof BooleanLocation);
+        } else if (destination instanceof TypedObjectLocation) {
+            return source instanceof TypedObjectLocation && ((TypedObjectLocation<?>) destination).getType().isAssignableFrom(((TypedObjectLocation<?>) source).getType());
+        } else if (destination instanceof ValueLocation) {
+            return false;
+        } else {
+            assert destination instanceof ObjectLocation || destination instanceof DualLocation;
+            return true;
+        }
+    }
+
+    protected Location existingLocationForValue(Object value, Location oldLocation, Shape oldShape) {
+        assert oldShape.getLayout() == this;
+        Location newLocation;
+        if (oldLocation instanceof IntLocation && value instanceof Integer) {
+            newLocation = oldLocation;
+        } else if (oldLocation instanceof DoubleLocation && (value instanceof Double || this.isAllowedIntToDouble() && value instanceof Integer)) {
+            newLocation = oldLocation;
+        } else if (oldLocation instanceof LongLocation && (value instanceof Long || this.isAllowedIntToLong() && value instanceof Long)) {
+            newLocation = oldLocation;
+        } else if (oldLocation instanceof DeclaredLocation) {
+            return oldShape.allocator().locationForValue(value, EnumSet.of(LocationModifier.Final, LocationModifier.NonNull));
+        } else if (oldLocation instanceof ConstantLocation) {
+            return LocationImpl.valueEquals(oldLocation.get(null, false), value) ? oldLocation : new Locations.ConstantLocation(value);
+        } else if (oldLocation instanceof TypedObjectLocation && !((TypedObjectLocation<?>) oldLocation).getType().isAssignableFrom(value.getClass())) {
+            newLocation = (((TypedObjectLocation<?>) oldLocation).toUntypedLocation());
+        } else if (oldLocation instanceof DualLocation) {
+            if (oldLocation.canStore(value)) {
+                newLocation = oldLocation;
+            } else {
+                newLocation = ((BaseAllocator) oldShape.allocator()).locationForValueUpcast(value, oldLocation);
+            }
+        } else if (oldLocation instanceof ObjectLocation) {
+            newLocation = oldLocation;
+        } else {
+            return oldShape.allocator().locationForValue(value, EnumSet.of(LocationModifier.NonNull));
+        }
+        if (newLocation instanceof EffectivelyFinalLocation) {
+            newLocation = ((EffectivelyFinalLocation<?>) newLocation).toNonFinalLocation();
+        }
+        return newLocation;
+    }
+
+    /**
+     * Is this property an upcast of the other property?
+     *
+     * @param other the property being compared to
+     * @return true if this is a upcast of the other property, false otherwise
+     */
+    public boolean isPropertyUpcastOf(Property thiz, Property other) {
+        if (thiz.getLocation() != null && other.getLocation() != null && other.getKey().equals(thiz.getKey()) && other.getFlags() == thiz.getFlags()) {
+            if (isLocationAssignableFrom(thiz.getLocation(), other.getLocation())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public abstract Allocator createAllocator();
+
+    public LayoutStrategy getStrategy() {
+        return strategy;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.ShapeImpl.*;
+
+public interface LayoutStrategy {
+    boolean updateShape(DynamicObject object);
+
+    Shape returnCached(Shape newShape);
+
+    Shape ensureSpace(Shape shape, Location location);
+
+    boolean isAutoExtArray();
+
+    Property generalizeProperty(DynamicObject object, Property oldProperty, Object value);
+
+    Property generalizeProperty(DynamicObject object, Property oldProperty, Object value, Shape oldShape, Shape newShape);
+
+    BaseAllocator createAllocator(Layout shape);
+
+    BaseAllocator createAllocator(Shape shape);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LocationImpl.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import com.oracle.truffle.api.object.*;
+
+public abstract class LocationImpl extends Location {
+    @Override
+    public void set(DynamicObject store, Object value, Shape shape) throws IncompatibleLocationException, FinalLocationException {
+        setInternal(store, value);
+    }
+
+    @Override
+    protected final Object getInternal(DynamicObject store) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected abstract void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException;
+
+    @Override
+    public final boolean canSet(DynamicObject store, Object value) {
+        return canStore(value) && canStoreFinal(store, value);
+    }
+
+    @Override
+    public boolean canStore(Object value) {
+        return true;
+    }
+
+    @SuppressWarnings("unused")
+    protected boolean canStoreFinal(DynamicObject store, Object value) {
+        return true;
+    }
+
+    public interface EffectivelyFinalLocation<T extends Location> {
+        T toNonFinalLocation();
+    }
+
+    public interface TypedObjectLocation<T extends Location & ObjectLocation> extends ObjectLocation {
+        T toUntypedLocation();
+    }
+
+    public interface InternalLongLocation extends LongLocation {
+        void setLongInternal(DynamicObject store, long value);
+    }
+
+    @Override
+    public boolean isFinal() {
+        return false;
+    }
+
+    @Override
+    public boolean isConstant() {
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + (isFinal() ? 1231 : 1237);
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        Location other = (Location) obj;
+        if (isFinal() != other.isFinal()) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        String finalString = isFinal() ? "f" : "";
+        String typeString = this instanceof IntLocation ? "i" : (this instanceof DoubleLocation ? "d" : (this instanceof BooleanLocation ? "b"
+                        : (this instanceof TypedLocation ? ((TypedLocation) this).getType().getSimpleName() : "o")));
+        return finalString + typeString + getWhereString();
+    }
+
+    protected String getWhereString() {
+        return "";
+    }
+
+    /**
+     * Get the number of object array elements this location requires.
+     */
+    public int objectArrayCount() {
+        return 0;
+    }
+
+    /**
+     * Get the number of in-object {@link Object} fields this location requires.
+     */
+    public int objectFieldCount() {
+        return 0;
+    }
+
+    /**
+     * Get the number of in-object primitive fields this location requires.
+     */
+    public int primitiveFieldCount() {
+        return 0;
+    }
+
+    /**
+     * Get the number of primitive array elements this location requires.
+     */
+    public int primitiveArrayCount() {
+        return 0;
+    }
+
+    /**
+     * Boxed values need to be compared by value not by reference.
+     *
+     * The first parameter should be the one with the more precise type information.
+     *
+     * For sets to final locations, otherValue.equals(thisValue) seems more beneficial, since we
+     * usually know more about the value to be set.
+     */
+    public static boolean valueEquals(Object val1, Object val2) {
+        return val1 == val2 || (val1 != null && val1.equals(val2));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Locations.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.object.*;
+
+/**
+ * Property location.
+ *
+ * @see Location
+ * @see Shape
+ * @see Property
+ * @see DynamicObject
+ */
+public abstract class Locations {
+    public abstract static class ValueLocation extends LocationImpl {
+
+        private final Object value;
+
+        public ValueLocation(Object value) {
+            assert !(value instanceof Location);
+            this.value = value;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + ((value == null) ? 0 : 0 /* value.hashCode() */);
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            ValueLocation other = (ValueLocation) obj;
+            if (value == null) {
+                if (other.value != null) {
+                    return false;
+                }
+            } else if (!value.equals(other.value)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public final Object get(DynamicObject store, boolean condition) {
+            return value;
+        }
+
+        @Override
+        public final void set(DynamicObject store, Object value, Shape shape) throws IncompatibleLocationException, FinalLocationException {
+            if (!canStoreFinal(store, value)) {
+                throw finalLocation();
+            }
+        }
+
+        @Override
+        protected boolean canStoreFinal(DynamicObject store, Object val) {
+            return valueEquals(this.value, val);
+        }
+
+        @Override
+        public final void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (!canStoreFinal(store, value)) {
+                CompilerDirectives.transferToInterpreter();
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "=" + String.valueOf(value);
+        }
+    }
+
+    public static final class ConstantLocation extends ValueLocation {
+
+        public ConstantLocation(Object value) {
+            super(value);
+        }
+
+        @Override
+        public boolean isConstant() {
+            return true;
+        }
+    }
+
+    public static final class DeclaredLocation extends ValueLocation {
+
+        public DeclaredLocation(Object value) {
+            super(value);
+        }
+    }
+
+    public static class DualLocation extends LocationImpl implements TypedLocation {
+        protected final InternalLongLocation primitiveLocation;
+        protected final ObjectLocation objectLocation;
+        protected final LayoutImpl layout;
+        private final Class<?> type;
+
+        public DualLocation(InternalLongLocation primitiveLocation, ObjectLocation objectLocation, LayoutImpl layout) {
+            this(primitiveLocation, objectLocation, layout, null);
+        }
+
+        public DualLocation(InternalLongLocation primitiveLocation, ObjectLocation objectLocation, LayoutImpl layout, Class<?> type) {
+            this.primitiveLocation = primitiveLocation;
+            this.objectLocation = objectLocation;
+            this.layout = layout;
+            this.type = type;
+        }
+
+        @Override
+        public Object get(DynamicObject store, boolean condition) {
+            if (type == Object.class) {
+                return objectLocation.get(store, condition);
+            } else {
+                long rawValue = primitiveLocation.getLong(store, condition);
+                if (type == int.class) {
+                    return (int) rawValue;
+                } else if (type == long.class) {
+                    return rawValue;
+                } else if (type == double.class) {
+                    return Double.longBitsToDouble(rawValue);
+                } else if (type == boolean.class) {
+                    return rawValue != 0;
+                } else {
+                    CompilerDirectives.transferToInterpreter();
+                    throw new IllegalStateException();
+                }
+            }
+        }
+
+        @Override
+        public void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (type == Object.class) {
+                ((LocationImpl) objectLocation).setInternal(store, value);
+            } else {
+                long rawValue;
+                if (type == int.class && value instanceof Integer) {
+                    rawValue = (int) value;
+                } else if (type == long.class && value instanceof Long) {
+                    rawValue = (long) value;
+                } else if (type == long.class && layout.isAllowedIntToLong() && value instanceof Integer) {
+                    rawValue = (int) value;
+                } else if (type == double.class && value instanceof Double) {
+                    rawValue = Double.doubleToRawLongBits((double) value);
+                } else if (type == double.class && layout.isAllowedIntToDouble() && value instanceof Integer) {
+                    rawValue = Double.doubleToRawLongBits((int) value);
+                } else if (type == boolean.class && value instanceof Boolean) {
+                    rawValue = (boolean) value ? 1 : 0;
+                } else {
+                    throw incompatibleLocation();
+                }
+
+                primitiveLocation.setLongInternal(store, rawValue);
+            }
+        }
+
+        @Override
+        public int primitiveFieldCount() {
+            return ((LocationImpl) primitiveLocation).primitiveFieldCount();
+        }
+
+        @Override
+        public int primitiveArrayCount() {
+            return ((LocationImpl) primitiveLocation).primitiveArrayCount();
+        }
+
+        @Override
+        public int objectFieldCount() {
+            return ((LocationImpl) objectLocation).objectFieldCount();
+        }
+
+        @Override
+        public int objectArrayCount() {
+            return ((LocationImpl) objectLocation).objectArrayCount();
+        }
+
+        @Override
+        public String toString() {
+            return objectLocation.toString() + "," + primitiveLocation.toString() + "," + type;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            DualLocation other = (DualLocation) obj;
+            return getObjectLocation().equals(other.getObjectLocation()) && primitiveLocation.equals(other.primitiveLocation) && layout.equals(other.layout) && Objects.equals(type, other.type);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + (getObjectLocation() == null ? 0 : getObjectLocation().hashCode());
+            result = prime * result + (primitiveLocation == null ? 0 : primitiveLocation.hashCode());
+            result = prime * result + (type == null ? 0 : type.hashCode());
+            return result;
+        }
+
+        public ObjectLocation getObjectLocation() {
+            return objectLocation;
+        }
+
+        public DualLocation changeType(Class<?> newType) {
+            return new DualLocation(primitiveLocation, objectLocation, layout, newType);
+        }
+
+        public Class<?> getType() {
+            return type;
+        }
+
+        public boolean isNonNull() {
+            return false;
+        }
+
+        @Override
+        public boolean canStore(Object value) {
+            if (type == null) {
+                return false;
+            } else if (type == int.class) {
+                return value instanceof Integer;
+            } else if (type == long.class) {
+                return value instanceof Long || (layout.isAllowedIntToLong() && value instanceof Integer);
+            } else if (type == double.class) {
+                return value instanceof Double || (layout.isAllowedIntToDouble() && value instanceof Integer);
+            } else if (type == boolean.class) {
+                return value instanceof Boolean;
+            } else if (type == Object.class) {
+                return true;
+            } else {
+                throw new IllegalStateException();
+            }
+        }
+    }
+
+    public static class DeclaredDualLocation extends DualLocation {
+        private final Object defaultValue;
+
+        public DeclaredDualLocation(InternalLongLocation primitiveLocation, ObjectLocation objectLocation, Object defaultValue, LayoutImpl layout) {
+            super(primitiveLocation, objectLocation, layout);
+            this.defaultValue = defaultValue;
+        }
+
+        @Override
+        public Object get(DynamicObject store, boolean condition) {
+            return defaultValue;
+        }
+
+        @Override
+        public void setInternal(DynamicObject store, Object value) throws IncompatibleLocationException {
+            if (valueEquals(defaultValue, value)) {
+                return;
+            } else {
+                throw incompatibleLocation();
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return super.equals(obj) && Objects.equals(defaultValue, ((DeclaredDualLocation) obj).defaultValue);
+        }
+
+        @Override
+        public int hashCode() {
+            return super.hashCode();
+        }
+
+        @Override
+        public DualLocation changeType(Class<?> newType) {
+            return new DualLocation(primitiveLocation, objectLocation, layout, newType);
+        }
+
+        @Override
+        public boolean canStore(Object value) {
+            return valueEquals(defaultValue, value);
+        }
+
+        @Override
+        public String toString() {
+            return objectLocation.toString() + "," + primitiveLocation.toString() + ",unset";
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/ObjectStorageOptions.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import static com.oracle.truffle.api.object.Layout.*;
+
+public class ObjectStorageOptions {
+    // Shape configuration
+    /** Primitive location switch. */
+    public static boolean PrimitiveLocations = booleanOption(OPTION_PREFIX + "PrimitiveLocations", true);
+    public static boolean IntegerLocations = booleanOption(OPTION_PREFIX + "IntegerLocations", true);
+    public static boolean DoubleLocations = booleanOption(OPTION_PREFIX + "DoubleLocations", true);
+    public static boolean LongLocations = booleanOption(OPTION_PREFIX + "LongLocations", true);
+    public static boolean BooleanLocations = booleanOption(OPTION_PREFIX + "BooleanLocations", true);
+    public static boolean TypedObjectLocations = booleanOption(OPTION_PREFIX + "TypedObjectLocations", true);
+
+    /** Allocation of in-object fields. */
+    public static boolean InObjectFields = booleanOption(OPTION_PREFIX + "InObjectFields", true);
+
+    // Debug options (should be final)
+    public static final boolean TraceReshape = booleanOption(OPTION_PREFIX + "TraceReshape", false);
+    public static final boolean DumpShapes = booleanOption(OPTION_PREFIX + "DumpShapes", false);
+
+    public static final boolean Profile = booleanOption(OPTION_PREFIX + "Profile", false);
+    public static final int ProfileTopResults = Integer.getInteger(OPTION_PREFIX + "ProfileTopResults", -1);
+
+    public static boolean booleanOption(String name, boolean defaultValue) {
+        String value = System.getProperty(name);
+        return value == null ? defaultValue : value.equalsIgnoreCase("true");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyImpl.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.Locations.*;
+
+/**
+ * Property objects represent the mapping between low-level stores and high-level data. The simplest
+ * Property could be nothing more than a map of one index to one property's value, but abstracting
+ * the interface allows for getter/setter methods, type-checked properties, and other such
+ * specialized and language-specific behavior. ECMAScript[8.6.1]
+ */
+public class PropertyImpl extends Property {
+    private final Object key;
+    private final Location location;
+    private final int flags;
+    private final boolean shadow;
+    private final boolean relocatable;
+
+    /**
+     * Generic, usual-case constructor for properties storing at least a name.
+     *
+     * @param key the name of the property
+     */
+    protected PropertyImpl(Object key, Location location, int flags, boolean shadow, boolean relocatable) {
+        this.key = Objects.requireNonNull(key);
+        this.location = Objects.requireNonNull(location);
+        this.flags = flags;
+        this.shadow = shadow;
+        this.relocatable = relocatable;
+    }
+
+    public PropertyImpl(Object name, Location location, int flags) {
+        this(name, location, flags, false, true);
+    }
+
+    @Override
+    public final Object getKey() {
+        return key;
+    }
+
+    @Override
+    public int getFlags() {
+        return flags;
+    }
+
+    @Override
+    public Property relocate(Location newLocation) {
+        if ((getLocation() == null || !getLocation().equals(newLocation)) && relocatable) {
+            return construct(getKey(), newLocation, getFlags());
+        }
+        return this;
+    }
+
+    @Override
+    public final Object get(DynamicObject store, Shape shape) {
+        return getLocation().get(store, shape);
+    }
+
+    @Override
+    public final Object get(DynamicObject store, boolean condition) {
+        return getLocation().get(store, condition);
+    }
+
+    @Override
+    public final void setInternal(DynamicObject store, Object value) {
+        try {
+            ((LocationImpl) getLocation()).setInternal(store, value);
+        } catch (IncompatibleLocationException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public final void set(DynamicObject store, Object value, Shape shape) throws IncompatibleLocationException, FinalLocationException {
+        assert shape == null || store.getShape() == shape : "wrong shape";
+        getLocation().set(store, value, shape);
+    }
+
+    @Override
+    public final void setSafe(DynamicObject store, Object value, Shape shape) {
+        assert shape == null || store.getShape() == shape : "wrong shape";
+        try {
+            getLocation().set(store, value, shape);
+        } catch (IncompatibleLocationException | FinalLocationException ex) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public final void setGeneric(DynamicObject store, Object value, Shape shape) {
+        assert shape == null || store.getShape() == shape : "wrong shape";
+        try {
+            set(store, value, shape);
+        } catch (IncompatibleLocationException | FinalLocationException ex) {
+            setSlowCase(store, value);
+        }
+    }
+
+    @Override
+    public final void set(DynamicObject store, Object value, Shape oldShape, Shape newShape) throws IncompatibleLocationException {
+        assert store.getShape() == oldShape : "wrong shape";
+        assert newShape.isValid();
+        assert getLocation() != null;
+        getLocation().set(store, value, oldShape, newShape);
+    }
+
+    @Override
+    public final void setSafe(DynamicObject store, Object value, Shape oldShape, Shape newShape) {
+        assert store.getShape() == oldShape : "wrong old shape";
+        assert newShape.isValid();
+        assert getLocation() != null;
+        try {
+            getLocation().set(store, value, oldShape, newShape);
+        } catch (IncompatibleLocationException ex) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @Override
+    public final void setGeneric(DynamicObject store, Object value, Shape oldShape, Shape newShape) {
+        assert store.getShape() == oldShape : "wrong old shape";
+        assert newShape.isValid();
+        assert getLocation() != null;
+        try {
+            getLocation().set(store, value, oldShape, newShape);
+        } catch (IncompatibleLocationException ex) {
+            setWithShapeSlowCase(store, value, oldShape, newShape);
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        PropertyImpl other = (PropertyImpl) obj;
+        return key.equals(other.key) && ((location == null && other.location == null) || (location != null && location.equals(other.location))) && flags == other.flags && shadow == other.shadow &&
+                        relocatable == other.relocatable;
+    }
+
+    @Override
+    public boolean isSame(Property obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        PropertyImpl other = (PropertyImpl) obj;
+        return key.equals(other.key) && flags == other.flags;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + getClass().hashCode();
+        result = prime * result + key.hashCode();
+        result = prime * result + (location != null ? location.hashCode() : 0);
+        result = prime * result + flags;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "\"" + key + "\"" + ":" + location;
+    }
+
+    @Override
+    public final Location getLocation() {
+        return location;
+    }
+
+    private void setSlowCase(DynamicObject store, Object value) {
+        if (getLocation() instanceof DeclaredLocation) {
+            setDeclaredLocation(store, value);
+        } else {
+            generalize(store, value);
+        }
+    }
+
+    private void setDeclaredLocation(DynamicObject store, Object value) {
+        store.updateShape();
+        Shape oldShape = store.getShape();
+        Shape newShape = oldShape.addProperty(this.relocateShadow(oldShape.allocator().locationForValue(value, EnumSet.of(LocationModifier.Final, LocationModifier.NonNull))));
+        store.updateShape();
+        newShape.getLastProperty().setGeneric(store, value, oldShape, newShape);
+    }
+
+    private Property generalize(DynamicObject store, Object value) {
+        return ((LayoutImpl) store.getShape().getLayout()).getStrategy().generalizeProperty(store, this, value);
+    }
+
+    private void setWithShapeSlowCase(DynamicObject store, Object value, Shape oldShape, Shape newShape) {
+        ((LayoutImpl) store.getShape().getLayout()).getStrategy().generalizeProperty(store, this, value, oldShape, newShape);
+    }
+
+    @Override
+    public final boolean isHidden() {
+        return key instanceof HiddenKey;
+    }
+
+    @Override
+    public final boolean isShadow() {
+        return shadow;
+    }
+
+    private Property relocateShadow(Location newLocation) {
+        assert !isShadow() && getLocation() instanceof DeclaredLocation && relocatable;
+        return new PropertyImpl(getKey(), newLocation, flags, true, relocatable);
+    }
+
+    @SuppressWarnings("hiding")
+    protected Property construct(Object name, Location location, int flags) {
+        return new PropertyImpl(name, location, flags, shadow, relocatable);
+    }
+
+    @Override
+    public Property copyWithFlags(int newFlags) {
+        return construct(key, location, newFlags);
+    }
+
+    @Override
+    public Property copyWithRelocatable(boolean newRelocatable) {
+        if (this.relocatable != newRelocatable) {
+            return new PropertyImpl(key, location, flags, shadow, newRelocatable);
+        }
+        return this;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyMap.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.object.*;
+
+public final class PropertyMap implements Map<Object, Property> {
+    private final PropertyMap car;
+    private final Property cdr;
+    private final int size;
+
+    private static final PropertyMap EMPTY = new PropertyMap();
+
+    private PropertyMap() {
+        this.car = null;
+        this.cdr = null;
+        this.size = 0;
+    }
+
+    private PropertyMap(PropertyMap parent, Property added) {
+        this.car = Objects.requireNonNull(parent);
+        this.cdr = added;
+        this.size = parent.size + 1;
+    }
+
+    public static PropertyMap empty() {
+        return EMPTY;
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    public boolean containsKey(Object key) {
+        for (Map.Entry<Object, Property> entry : reverseOrderEntrySet()) {
+            if (entry.getKey().equals(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean containsValue(Object value) {
+        for (Map.Entry<Object, Property> entry : reverseOrderEntrySet()) {
+            if (entry.getValue().equals(value)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Property get(Object key) {
+        for (Map.Entry<Object, Property> entry : reverseOrderEntrySet()) {
+            if (entry.getKey().equals(key)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    public Property put(Object key, Property value) {
+        throw unmodifiableException();
+    }
+
+    public Property remove(Object key) {
+        throw unmodifiableException();
+    }
+
+    public void putAll(Map<? extends Object, ? extends Property> m) {
+        throw unmodifiableException();
+    }
+
+    public void clear() {
+        throw unmodifiableException();
+    }
+
+    public Set<Object> keySet() {
+        return new AbstractSet<Object>() {
+            @Override
+            public Iterator<Object> iterator() {
+                Object[] keys = new Object[size()];
+                Iterator<Map.Entry<Object, Property>> iterator = reverseOrderEntrySet().iterator();
+                for (int pos = size() - 1; pos >= 0; pos--) {
+                    keys[pos] = iterator.next().getKey();
+                }
+                return Arrays.asList(keys).iterator();
+            }
+
+            @Override
+            public int size() {
+                return PropertyMap.this.size();
+            }
+        };
+    }
+
+    public Collection<Property> values() {
+        return new AbstractSet<Property>() {
+            @Override
+            public Iterator<Property> iterator() {
+                Property[] values = new Property[size()];
+                Iterator<Map.Entry<Object, Property>> iterator = reverseOrderEntrySet().iterator();
+                for (int pos = size() - 1; pos >= 0; pos--) {
+                    values[pos] = iterator.next().getValue();
+                }
+                return Arrays.asList(values).iterator();
+            }
+
+            @Override
+            public int size() {
+                return PropertyMap.this.size();
+            }
+        };
+    }
+
+    public Set<Map.Entry<Object, Property>> entrySet() {
+        return new AbstractSet<Map.Entry<Object, Property>>() {
+            @Override
+            public Iterator<Map.Entry<Object, Property>> iterator() {
+                @SuppressWarnings("unchecked")
+                Map.Entry<Object, Property>[] entries = (Map.Entry<Object, Property>[]) new Map.Entry<?, ?>[size()];
+                Iterator<Map.Entry<Object, Property>> iterator = reverseOrderEntrySet().iterator();
+                for (int pos = size() - 1; pos >= 0; pos--) {
+                    entries[pos] = iterator.next();
+                }
+                return Arrays.asList(entries).iterator();
+            }
+
+            @Override
+            public int size() {
+                return PropertyMap.this.size();
+            }
+        };
+    }
+
+    public Set<Map.Entry<Object, Property>> reverseOrderEntrySet() {
+        return new AbstractSet<Map.Entry<Object, Property>>() {
+            @Override
+            public Iterator<Map.Entry<Object, Property>> iterator() {
+                return new Iterator<Map.Entry<Object, Property>>() {
+                    PropertyMap current = PropertyMap.this;
+
+                    public Entry<Object, Property> next() {
+                        if (hasNext()) {
+                            try {
+                                return new MapEntryImpl(current.cdr);
+                            } finally {
+                                current = current.car;
+                            }
+                        } else {
+                            throw new NoSuchElementException();
+                        }
+                    }
+
+                    public boolean hasNext() {
+                        return current != empty();
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+
+            @Override
+            public int size() {
+                return PropertyMap.this.size();
+            }
+        };
+    }
+
+    public Set<Object> reverseOrderKeys() {
+        return new AbstractSet<Object>() {
+            @Override
+            public Iterator<Object> iterator() {
+                return new Iterator<Object>() {
+                    PropertyMap current = PropertyMap.this;
+
+                    public Object next() {
+                        if (hasNext()) {
+                            try {
+                                return current.cdr.getKey();
+                            } finally {
+                                current = current.car;
+                            }
+                        } else {
+                            throw new NoSuchElementException();
+                        }
+                    }
+
+                    public boolean hasNext() {
+                        return current != empty();
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+
+            @Override
+            public int size() {
+                return PropertyMap.this.size();
+            }
+        };
+    }
+
+    public Set<Property> reverseOrderValues() {
+        return new AbstractSet<Property>() {
+            @Override
+            public Iterator<Property> iterator() {
+                return new Iterator<Property>() {
+                    PropertyMap current = PropertyMap.this;
+
+                    public Property next() {
+                        if (hasNext()) {
+                            try {
+                                return current.cdr;
+                            } finally {
+                                current = current.car;
+                            }
+                        } else {
+                            throw new NoSuchElementException();
+                        }
+                    }
+
+                    public boolean hasNext() {
+                        return current != empty();
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+
+            @Override
+            public int size() {
+                return PropertyMap.this.size();
+            }
+        };
+    }
+
+    private static final class MapEntryImpl implements Map.Entry<Object, Property> {
+        private final Property backingProperty;
+
+        public MapEntryImpl(Property backingProperty) {
+            this.backingProperty = backingProperty;
+        }
+
+        public Object getKey() {
+            return backingProperty.getKey();
+        }
+
+        public Property getValue() {
+            return backingProperty;
+        }
+
+        public Property setValue(Property value) {
+            throw unmodifiableException();
+        }
+    }
+
+    private static UnsupportedOperationException unmodifiableException() {
+        throw new UnsupportedOperationException("unmodifiable");
+    }
+
+    public PropertyMap putCopy(Property value) {
+        assert !this.containsValue(value);
+        return new PropertyMap(this, value);
+    }
+
+    public PropertyMap removeCopy(Property value) {
+        LinkedList<Property> shelve = new LinkedList<>();
+        PropertyMap current = this;
+        while (!current.isEmpty()) {
+            if (current.getLastProperty().equals(value)) {
+                PropertyMap newMap = current.getParentMap();
+                for (Property property : shelve) {
+                    newMap = newMap.putCopy(property);
+                }
+                return newMap;
+            } else {
+                shelve.push(current.getLastProperty());
+                current = current.getParentMap();
+            }
+        }
+        return this;
+    }
+
+    public PropertyMap replaceCopy(Property oldValue, Property newValue) {
+        LinkedList<Property> shelve = new LinkedList<>();
+        PropertyMap current = this;
+        while (!current.isEmpty()) {
+            if (current.getLastProperty().equals(oldValue)) {
+                PropertyMap newMap = current.getParentMap();
+                newMap = newMap.putCopy(newValue);
+                for (Property property : shelve) {
+                    newMap = newMap.putCopy(property);
+                }
+                return newMap;
+            } else {
+                shelve.push(current.getLastProperty());
+                current = current.getParentMap();
+            }
+        }
+        return this;
+    }
+
+    public PropertyMap getOwningMap(Property value) {
+        PropertyMap current = this;
+        while (!current.isEmpty()) {
+            if (current.getLastProperty().equals(value)) {
+                return current;
+            }
+            current = current.getParentMap();
+        }
+        return null;
+    }
+
+    PropertyMap getParentMap() {
+        return car;
+    }
+
+    public Property getLastProperty() {
+        return cdr;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/ShapeImpl.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (c) 2012, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.LocationImpl.InternalLongLocation;
+import com.oracle.truffle.object.Locations.ConstantLocation;
+import com.oracle.truffle.object.Locations.DeclaredDualLocation;
+import com.oracle.truffle.object.Locations.DeclaredLocation;
+import com.oracle.truffle.object.Locations.DualLocation;
+import com.oracle.truffle.object.Locations.ValueLocation;
+import com.oracle.truffle.object.Transition.AddTransition;
+import com.oracle.truffle.object.Transition.OperationsTransition;
+import com.oracle.truffle.object.Transition.PropertyTransition;
+import com.oracle.truffle.object.Transition.RemoveTransition;
+
+/**
+ * Shape objects create a mapping of Property objects to indexes. The mapping of those indexes to an
+ * actual store is not part of Shape's role, but JSObject's. Shapes are immutable; adding or
+ * deleting a property yields a new Shape which links to the old one. This allows inline caching to
+ * simply check the identity of an object's Shape to determine if the cache is valid. There is one
+ * exception to this immutability, the transition map, but that is used simply to assure that an
+ * identical series of property additions and deletions will yield the same Shape object.
+ *
+ * @see DynamicObject
+ * @see Property
+ * @see Locations
+ */
+public abstract class ShapeImpl extends Shape {
+    private final int id;
+
+    protected final LayoutImpl layout;
+    protected final ObjectType objectType;
+    protected final ShapeImpl parent;
+    protected final PropertyMap propertyMap;
+
+    private final Object extraData;
+    private final Object sharedData;
+    private final ShapeImpl root;
+
+    protected final int objectArraySize;
+    protected final int objectArrayCapacity;
+    protected final int objectFieldSize;
+    protected final int primitiveFieldSize;
+    protected final int primitiveArraySize;
+    protected final int primitiveArrayCapacity;
+    protected final boolean hasPrimitiveArray;
+
+    protected final int depth;
+    protected final int propertyCount;
+    protected Property[] propertyArray;
+
+    protected final Assumption validAssumption;
+    protected final Assumption leafAssumption;
+
+    /**
+     * Shape transition map; lazily initialized.
+     *
+     * @see #getTransitionMapForRead()
+     * @see #getTransitionMapForWrite()
+     */
+    private HashMap<Transition, ShapeImpl> transitionMap;
+
+    /**
+     * Private constructor.
+     *
+     * @see #ShapeImpl(Layout, ShapeImpl, ObjectType, Object, PropertyMap, BaseAllocator, int)
+     */
+    private ShapeImpl(Layout layout, ShapeImpl parent, ObjectType operations, Object sharedData, PropertyMap propertyMap, int objectArraySize, int objectFieldSize, int primitiveFieldSize,
+                    int primitiveArraySize, boolean hasPrimitiveArray, int id) {
+        this.layout = (LayoutImpl) layout;
+        this.objectType = Objects.requireNonNull(operations);
+        this.propertyMap = Objects.requireNonNull(propertyMap);
+        this.root = parent != null ? parent.getRoot() : this;
+        this.parent = parent;
+        this.objectArraySize = objectArraySize;
+        this.objectArrayCapacity = capacityFromSize(objectArraySize);
+        this.objectFieldSize = objectFieldSize;
+        this.primitiveFieldSize = primitiveFieldSize;
+        this.primitiveArraySize = primitiveArraySize;
+        this.primitiveArrayCapacity = capacityFromSize(primitiveArraySize);
+        this.hasPrimitiveArray = hasPrimitiveArray;
+
+        if (parent != null) {
+            this.propertyCount = makePropertyCount(parent, propertyMap);
+            this.propertyArray = makePropertiesList(parent, propertyMap);
+            this.depth = parent.depth + 1;
+        } else {
+            this.propertyCount = 0;
+            this.propertyArray = null;
+            this.depth = 0;
+        }
+
+        this.validAssumption = createValidAssumption();
+        this.leafAssumption = createLeafAssumption();
+
+        this.id = id;
+        shapeCount.inc();
+
+        this.sharedData = sharedData;
+        this.extraData = operations.createShapeData(this);
+
+        debugRegisterShape(this);
+    }
+
+    protected ShapeImpl(Layout layout, ShapeImpl parent, ObjectType operations, Object sharedData, PropertyMap propertyMap, Allocator allocator, int id) {
+        this(layout, parent, operations, sharedData, propertyMap, ((BaseAllocator) allocator).objectArraySize, ((BaseAllocator) allocator).objectFieldSize,
+                        ((BaseAllocator) allocator).primitiveFieldSize, ((BaseAllocator) allocator).primitiveArraySize, ((BaseAllocator) allocator).hasPrimitiveArray, id);
+    }
+
+    @SuppressWarnings("hiding")
+    protected abstract ShapeImpl createShape(Layout layout, Object sharedData, ShapeImpl parent, ObjectType operations, PropertyMap propertyMap, Allocator allocator, int id);
+
+    protected ShapeImpl(Layout layout, ObjectType operations, Object sharedData, int id) {
+        this(layout, null, operations, sharedData, PropertyMap.empty(), layout.createAllocator(), id);
+    }
+
+    private static int makePropertyCount(ShapeImpl parent, PropertyMap propertyMap) {
+        return parent.propertyCount + ((propertyMap.size() > parent.propertyMap.size() && !propertyMap.getLastProperty().isHidden() && !propertyMap.getLastProperty().isShadow()) ? 1 : 0);
+    }
+
+    private static Property[] makePropertiesList(ShapeImpl parent, PropertyMap propertyMap) {
+        Property[] properties = parent.propertyArray;
+        if (properties != null && propertyMap.size() != parent.propertyMap.size()) {
+            Property lastProperty = propertyMap.getLastProperty();
+            if (lastProperty != null && !lastProperty.isHidden()) {
+                propertyListAllocCount.inc();
+                if (!lastProperty.isShadow()) {
+                    properties = Arrays.copyOf(properties, properties.length + 1);
+                    properties[properties.length - 1] = lastProperty;
+                } else {
+                    properties = Arrays.copyOf(properties, properties.length);
+                    for (int i = 0; i < properties.length; i++) {
+                        if (properties[i].isSame(lastProperty)) {
+                            properties[i] = lastProperty;
+                        }
+                    }
+                }
+            } else {
+                propertyListShareCount.inc();
+            }
+        }
+        return properties;
+    }
+
+    @Override
+    public final Property getLastProperty() {
+        return propertyMap.getLastProperty();
+    }
+
+    @Override
+    public final int getId() {
+        return this.id;
+    }
+
+    /**
+     * Calculate array size for the given number of elements.
+     */
+    private static int capacityFromSize(int size) {
+        if (size == 0) {
+            return 0;
+        } else if (size < 4) {
+            return 4;
+        } else if (size < 32) {
+            return ((size + 7) / 8) * 8;
+        } else {
+            return ((size + 15) / 16) * 16;
+        }
+    }
+
+    @Override
+    public final int getObjectArraySize() {
+        return objectArraySize;
+    }
+
+    @Override
+    public final int getObjectFieldSize() {
+        return objectFieldSize;
+    }
+
+    @Override
+    public final int getPrimitiveFieldSize() {
+        return primitiveFieldSize;
+    }
+
+    @Override
+    public final int getObjectArrayCapacity() {
+        return objectArrayCapacity;
+    }
+
+    @Override
+    public final int getPrimitiveArrayCapacity() {
+        return primitiveArrayCapacity;
+    }
+
+    @Override
+    public final int getPrimitiveArraySize() {
+        return primitiveArraySize;
+    }
+
+    @Override
+    public final boolean hasPrimitiveArray() {
+        return hasPrimitiveArray;
+    }
+
+    /**
+     * Get the (parent) shape that holds the given property.
+     */
+    public final ShapeImpl getShapeFromProperty(Object propertyName) {
+        ShapeImpl current = this;
+        while (current != getRoot()) {
+            if (current.getLastProperty().getKey().equals(propertyName)) {
+                return current;
+            }
+            current = current.getParent();
+        }
+
+        return null;
+    }
+
+    /**
+     * Get the (parent) shape that holds the given property.
+     */
+    public final ShapeImpl getShapeFromProperty(Property prop) {
+        ShapeImpl current = this;
+        while (current != getRoot()) {
+            if (current.getLastProperty().equals(prop)) {
+                return current;
+            }
+            current = current.parent;
+        }
+
+        return null;
+    }
+
+    /**
+     * Get a property entry by string name.
+     *
+     * @param key the name to look up
+     * @return a Property object, or null if not found
+     */
+    @Override
+    @TruffleBoundary
+    public final Property getProperty(Object key) {
+        // return this.propertyMap.get(propertyName);
+        PropertyMap current = this.propertyMap;
+        while (current.getLastProperty() != null) {
+            if (current.getLastProperty().getKey().equals(key)) {
+                return current.getLastProperty();
+            }
+            current = current.getParentMap();
+        }
+
+        return null;
+    }
+
+    protected final void addTransition(Transition transition, ShapeImpl next) {
+        assert next.getParent() == this;
+        addTransitionInternal(transition, next);
+    }
+
+    public final void addNonlinearTransition(Transition transition, ShapeImpl next) {
+        assert next.getParent() != this;
+        addTransitionInternal(transition, next);
+    }
+
+    private void addTransitionInternal(Transition transition, ShapeImpl next) {
+        getTransitionMapForWrite().put(transition, next);
+    }
+
+    public final Map<Transition, ShapeImpl> getTransitionMapForRead() {
+        return transitionMap != null ? transitionMap : Collections.<Transition, ShapeImpl> emptyMap();
+    }
+
+    private Map<Transition, ShapeImpl> getTransitionMapForWrite() {
+        if (transitionMap != null) {
+            return transitionMap;
+        } else {
+            invalidateLeafAssumption();
+            return transitionMap = new HashMap<>();
+        }
+    }
+
+    public final PropertyMap getPropertyMap() {
+        return propertyMap;
+    }
+
+    /**
+     * Add a new property in the map, yielding a new or cached Shape object.
+     *
+     * @param property the property to add
+     * @return the new Shape
+     */
+    @TruffleBoundary
+    @Override
+    public ShapeImpl addProperty(Property property) {
+        assert isValid();
+        ShapeImpl nextShape = addPropertyInternal(property);
+        objectType.onPropertyAdded(property, this, nextShape);
+        return nextShape;
+    }
+
+    /**
+     * Add a new property in the map, yielding a new or cached Shape object.
+     *
+     * In contrast to {@link ShapeImpl#addProperty(Property)}, this method does not care about
+     * obsolete shapes.
+     *
+     * @see #addProperty(Property)
+     */
+    private ShapeImpl addPropertyInternal(Property prop) {
+        CompilerAsserts.neverPartOfCompilation();
+        assert prop.isShadow() || !(this.hasProperty(prop.getKey())) : "duplicate property";
+        assert !getPropertyListInternal(false).contains(prop);
+        // invalidatePropertyAssumption(prop.getName());
+
+        Transition.AddTransition key = new Transition.AddTransition(prop);
+        Map<Transition, ShapeImpl> transitionMapForRead = this.getTransitionMapForRead();
+        ShapeImpl cachedShape = transitionMapForRead.get(key);
+        if (cachedShape != null) { // Shape already exists?
+            shapeCacheHitCount.inc();
+            return (ShapeImpl) layout.getStrategy().returnCached(cachedShape);
+        }
+        shapeCacheMissCount.inc();
+
+        ShapeImpl oldShape = (ShapeImpl) layout.getStrategy().ensureSpace(this, prop.getLocation());
+
+        ShapeImpl newShape = makeShapeWithAddedProperty(oldShape, key);
+        oldShape.addTransition(key, newShape);
+        return newShape;
+    }
+
+    protected ShapeImpl cloneRoot(ShapeImpl from, Object newSharedData) {
+        return createShape(from.layout, newSharedData, null, from.objectType, from.propertyMap, from.allocator(), from.id);
+    }
+
+    /**
+     * Create a separate clone of a shape.
+     *
+     * @param newParent the cloned parent shape
+     */
+    protected final ShapeImpl cloneOnto(ShapeImpl newParent) {
+        ShapeImpl from = this;
+        ShapeImpl newShape = createShape(newParent.layout, newParent.sharedData, newParent, from.objectType, from.propertyMap, from.allocator(), newParent.id);
+
+        shapeCloneCount.inc();
+
+        // (aw) need to have this transition for obsolescence
+        newParent.addTransition(getTransitionFromParent(), newShape);
+        return newShape;
+    }
+
+    private Transition getTransitionFromParent() {
+        for (Map.Entry<Transition, ShapeImpl> property : parent.getTransitionMapForRead().entrySet()) {
+            if (property.getValue() == this) {
+                return property.getKey();
+            }
+        }
+        throw new NoSuchElementException();
+    }
+
+    /**
+     * Create a new shape that adds a property to the parent shape.
+     */
+    private static ShapeImpl makeShapeWithAddedProperty(ShapeImpl parent, AddTransition addTransition) {
+        Property addend = addTransition.getProperty();
+        BaseAllocator allocator = parent.allocator().addLocation(addend.getLocation());
+
+        PropertyMap newPropertyMap = parent.propertyMap.putCopy(addend);
+
+        ShapeImpl newShape = parent.createShape(parent.layout, parent.sharedData, parent, parent.objectType, newPropertyMap, allocator, parent.id);
+        assert ((LocationImpl) addend.getLocation()).primitiveArrayCount() == 0 || newShape.hasPrimitiveArray;
+        assert newShape.depth == allocator.depth;
+        return newShape;
+    }
+
+    /**
+     * Create a new shape that reserves the primitive extension array field.
+     */
+    private static ShapeImpl makeShapeWithPrimitiveExtensionArray(ShapeImpl parent) {
+        assert parent.getLayout().hasPrimitiveExtensionArray();
+        assert !parent.hasPrimitiveArray();
+        BaseAllocator allocator = parent.allocator().addLocation(parent.getLayout().getPrimitiveArrayLocation());
+        ShapeImpl newShape = parent.createShape(parent.layout, parent.sharedData, parent, parent.objectType, parent.propertyMap, allocator, parent.id);
+        assert newShape.hasPrimitiveArray();
+        assert newShape.depth == allocator.depth;
+        return newShape;
+    }
+
+    private ShapeImpl addPrimitiveExtensionArray() {
+        assert layout.hasPrimitiveExtensionArray() && !hasPrimitiveArray();
+        Transition key = new Transition.ReservePrimitiveArrayTransition();
+        ShapeImpl cachedShape = this.getTransitionMapForRead().get(key);
+        if (cachedShape != null) {
+            shapeCacheHitCount.inc();
+            return (ShapeImpl) layout.getStrategy().returnCached(cachedShape);
+        }
+        shapeCacheMissCount.inc();
+
+        ShapeImpl oldShape = (ShapeImpl) layout.getStrategy().ensureSpace(this, layout.getPrimitiveArrayLocation());
+        ShapeImpl newShape = makeShapeWithPrimitiveExtensionArray(oldShape);
+        oldShape.addTransition(key, newShape);
+        return newShape;
+    }
+
+    /**
+     * Are these two shapes related, i.e. do they have the same root?
+     *
+     * @param other Shape to compare to
+     * @return true if one shape is an upcast of the other, or the Shapes are equal
+     */
+    @Override
+    public boolean isRelated(Shape other) {
+        if (this == other) {
+            return true;
+        }
+        if (this.getRoot() == getRoot()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Get a list of all properties that this Shape stores.
+     *
+     * @return list of properties
+     */
+    @TruffleBoundary
+    @Override
+    public final List<Property> getPropertyList(Pred<Property> filter) {
+        LinkedList<Property> props = new LinkedList<>();
+        next: for (ShapeImpl current = this; current != getRoot(); current = current.parent) {
+            Property currentProperty = current.getLastProperty();
+            if (!currentProperty.isHidden() && filter.test(currentProperty)) {
+                if (currentProperty.getLocation() instanceof DeclaredLocation) {
+                    for (Iterator<Property> iter = props.iterator(); iter.hasNext();) {
+                        Property other = iter.next();
+                        if (other.isShadow() && other.getKey().equals(currentProperty.getKey())) {
+                            iter.remove();
+                            props.addFirst(other);
+                            continue next;
+                        }
+                    }
+                }
+                props.addFirst(currentProperty);
+            }
+        }
+        return props;
+    }
+
+    @Override
+    public final List<Property> getPropertyList() {
+        return getPropertyList(ALL);
+    }
+
+    /**
+     * Returns all (also hidden) Property objects in this shape.
+     *
+     * @param ascending desired order
+     */
+    @TruffleBoundary
+    @Override
+    public final List<Property> getPropertyListInternal(boolean ascending) {
+        LinkedList<Property> props = new LinkedList<>();
+        for (ShapeImpl current = this; current != getRoot(); current = current.parent) {
+            if (ascending) {
+                props.addFirst(current.getLastProperty());
+            } else {
+                props.add(current.getLastProperty());
+            }
+        }
+        return props;
+    }
+
+    /**
+     * Get a list of all (visible) property names in insertion order.
+     *
+     * @return list of property names
+     */
+    @TruffleBoundary
+    @Override
+    public final List<Object> getKeyList(Pred<Property> filter) {
+        LinkedList<Object> keys = new LinkedList<>();
+        for (ShapeImpl current = this; current != getRoot(); current = current.parent) {
+            Property currentProperty = current.getLastProperty();
+            if (!currentProperty.isHidden() && filter.test(currentProperty) && !currentProperty.isShadow()) {
+                keys.addFirst(currentProperty.getKey());
+            }
+        }
+        return keys;
+    }
+
+    @Override
+    public final List<Object> getKeyList() {
+        return getKeyList(ALL);
+    }
+
+    @Override
+    public Iterable<Object> getKeys() {
+        return getKeyList();
+    }
+
+    public final void setTypeTransition(ShapeImpl successorShape, Property before, Property after) {
+        getTransitionMapForWrite().put(new Transition.TypeTransition(before, after), successorShape);
+    }
+
+    private static Assumption createValidAssumption() {
+        return Truffle.getRuntime().createAssumption("valid shape");
+    }
+
+    @Override
+    public final Assumption getValidAssumption() {
+        return validAssumption;
+    }
+
+    @Override
+    public final boolean isValid() {
+        return getValidAssumption().isValid();
+    }
+
+    private static Assumption createLeafAssumption() {
+        return Truffle.getRuntime().createAssumption("leaf shape");
+    }
+
+    @Override
+    public final Assumption getLeafAssumption() {
+        return leafAssumption;
+    }
+
+    @Override
+    public final boolean isLeaf() {
+        return getLeafAssumption().isValid();
+    }
+
+    private void invalidateLeafAssumption() {
+        getLeafAssumption().invalidate();
+    }
+
+    @Override
+    public String toString() {
+        return toStringLimit(Integer.MAX_VALUE);
+    }
+
+    @TruffleBoundary
+    public String toStringLimit(int limit) {
+        StringBuilder sb = new StringBuilder();
+        sb.append('@');
+        sb.append(Integer.toHexString(hashCode()));
+        if (!isValid()) {
+            sb.append('!');
+        }
+
+        sb.append("{");
+        for (Iterator<Property> iterator = propertyMap.reverseOrderValues().iterator(); iterator.hasNext();) {
+            Property p = iterator.next();
+            sb.append(p);
+            if (iterator.hasNext()) {
+                sb.append(", ");
+            }
+            if (sb.length() >= limit) {
+                sb.append("...");
+                break;
+            }
+            sb.append("\n");
+        }
+        sb.append("}");
+
+        return sb.toString();
+    }
+
+    @Override
+    public final ShapeImpl getParent() {
+        return parent;
+    }
+
+    public final int getDepth() {
+        return depth;
+    }
+
+    @Override
+    public final boolean hasProperty(Object name) {
+        return getProperty(name) != null;
+    }
+
+    @TruffleBoundary
+    @Override
+    public final ShapeImpl removeProperty(Property prop) {
+        RemoveTransition transition = new RemoveTransition(prop);
+        ShapeImpl cachedShape = getTransitionMapForRead().get(transition);
+        if (cachedShape != null) {
+            return (ShapeImpl) layout.getStrategy().returnCached(cachedShape);
+        }
+
+        ShapeImpl shape = getShapeFromProperty(prop);
+        if (shape != null) {
+            List<Property> properties = new ArrayList<>();
+            ShapeImpl current = this;
+            while (current != shape) {
+                properties.add(current.getLastProperty());
+                current = current.parent;
+            }
+            ShapeImpl newShape = shape.parent;
+            for (ListIterator<Property> iterator = properties.listIterator(properties.size()); iterator.hasPrevious();) {
+                Property previous = iterator.previous();
+                newShape = newShape.append(previous);
+            }
+
+            getTransitionMapForWrite().put(transition, newShape);
+            return newShape;
+        } else {
+            return null;
+        }
+    }
+
+    @TruffleBoundary
+    @Override
+    public final ShapeImpl append(Property oldProperty) {
+        return addProperty(oldProperty.relocate(allocator().moveLocation(oldProperty.getLocation())));
+    }
+
+    @Override
+    public final BaseAllocator allocator() {
+        return layout.getStrategy().createAllocator(this);
+    }
+
+    /**
+     * Duplicate shape exchanging existing property with new property.
+     */
+    @Override
+    public final ShapeImpl replaceProperty(Property oldProperty, Property newProp) {
+        ShapeImpl top = this;
+        List<Property> propertyList = new ArrayList<>();
+        boolean found = false;
+        while (top != getRoot() && !found) {
+            Property prop = top.getLastProperty();
+            propertyList.add(prop);
+            if (prop.getKey().equals(newProp.getKey())) {
+                found = true;
+            }
+            top = top.parent;
+        }
+        ShapeImpl newShape = top;
+        for (ListIterator<Property> iterator = propertyList.listIterator(propertyList.size()); iterator.hasPrevious();) {
+            Property prop = iterator.previous();
+            if (prop.getKey().equals(newProp.getKey())) {
+                newShape = newShape.addProperty(newProp);
+            } else {
+                newShape = newShape.addProperty(prop);
+            }
+        }
+        return newShape;
+    }
+
+    /**
+     * Find lowest common ancestor of two related shapes.
+     */
+    public static ShapeImpl findCommonAncestor(ShapeImpl left, ShapeImpl right) {
+        if (!left.isRelated(right)) {
+            throw new IllegalArgumentException("shapes must have the same root");
+        } else if (left == right) {
+            return left;
+        }
+        int leftLength = left.depth;
+        int rightLength = right.depth;
+        ShapeImpl leftPtr = left;
+        ShapeImpl rightPtr = right;
+        while (leftLength > rightLength) {
+            leftPtr = leftPtr.parent;
+            leftLength--;
+        }
+        while (rightLength > leftLength) {
+            rightPtr = rightPtr.parent;
+            rightLength--;
+        }
+        while (leftPtr != rightPtr) {
+            leftPtr = leftPtr.parent;
+            rightPtr = rightPtr.parent;
+        }
+        return leftPtr;
+    }
+
+    /**
+     * For copying over properties after exchanging the prototype of an object.
+     */
+    @TruffleBoundary
+    @Override
+    public final ShapeImpl copyOverPropertiesInternal(Shape destination) {
+        assert ((ShapeImpl) destination).getDepth() == 0;
+        List<Property> properties = this.getPropertyListInternal(true);
+        ShapeImpl newShape = ((ShapeImpl) destination).addPropertiesInternal(properties);
+        return newShape;
+    }
+
+    private ShapeImpl addPropertiesInternal(List<Property> properties) {
+        ShapeImpl newShape = this;
+        for (Property p : properties) {
+            newShape = newShape.addPropertyInternal(p);
+        }
+        return newShape;
+    }
+
+    /**
+     * NB: this is not an accurate property count.
+     */
+    @Override
+    public final int getPropertyCount() {
+        return propertyCount;
+    }
+
+    /**
+     * Find difference between two shapes.
+     *
+     * @see ObjectStorageOptions#TraceReshape
+     */
+    public static List<Property> diff(Shape oldShape, Shape newShape) {
+        List<Property> oldList = oldShape.getPropertyListInternal(false);
+        List<Property> newList = newShape.getPropertyListInternal(false);
+
+        List<Property> diff = new ArrayList<>(oldList);
+        diff.addAll(newList);
+        List<Property> intersection = new ArrayList<>(oldList);
+        intersection.retainAll(newList);
+        diff.removeAll(intersection);
+        return diff;
+    }
+
+    @Override
+    public ObjectType getObjectType() {
+        return objectType;
+    }
+
+    @Override
+    public ShapeImpl getRoot() {
+        return root;
+    }
+
+    @Override
+    public final boolean check(DynamicObject subject) {
+        return subject.getShape() == this;
+    }
+
+    @Override
+    public final LayoutImpl getLayout() {
+        return layout;
+    }
+
+    @Override
+    public final Object getData() {
+        return extraData;
+    }
+
+    @Override
+    public final Object getSharedData() {
+        return sharedData;
+    }
+
+    @TruffleBoundary
+    @Override
+    public final boolean hasTransitionWithKey(Object key) {
+        for (Transition transition : getTransitionMapForRead().keySet()) {
+            if (transition instanceof PropertyTransition) {
+                if (((PropertyTransition) transition).getProperty().getKey().equals(key)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Clone off a separate shape with new shared data.
+     */
+    @TruffleBoundary
+    @Override
+    public final ShapeImpl createSeparateShape(Object newSharedData) {
+        if (parent == null) {
+            return cloneRoot(this, newSharedData);
+        } else {
+            return this.cloneOnto(parent.createSeparateShape(newSharedData));
+        }
+    }
+
+    @Override
+    @TruffleBoundary
+    public final ShapeImpl changeType(ObjectType newOps) {
+        OperationsTransition transition = new OperationsTransition(newOps);
+        ShapeImpl cachedShape = getTransitionMapForRead().get(transition);
+        if (cachedShape != null) {
+            return cachedShape;
+        } else {
+            ShapeImpl newShape = createShape(layout, extraData, this, newOps, propertyMap, allocator(), id);
+            addTransition(transition, newShape);
+            return newShape;
+        }
+    }
+
+    @Override
+    public final Shape reservePrimitiveExtensionArray() {
+        if (layout.hasPrimitiveExtensionArray() && !hasPrimitiveArray()) {
+            return addPrimitiveExtensionArray();
+        }
+        return this;
+    }
+
+    @Override
+    public final Iterable<Property> getProperties() {
+        if (getPropertyCount() != 0 && propertyArray == null) {
+            CompilerDirectives.transferToInterpreter();
+            propertyArray = createPropertiesArray();
+        }
+        return new Iterable<Property>() {
+            public Iterator<Property> iterator() {
+                return new Iterator<Property>() {
+                    private int cursor;
+
+                    public boolean hasNext() {
+                        return cursor < getPropertyCount();
+                    }
+
+                    public Property next() {
+                        if (hasNext()) {
+                            return propertyArray[cursor++];
+                        }
+                        throw new NoSuchElementException();
+                    }
+
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    private Property[] createPropertiesArray() {
+        propertyListAllocCount.inc();
+        Property[] propertiesArray = new Property[getPropertyCount()];
+        List<Property> ownProperties = getPropertyList(ALL);
+        assert ownProperties.size() == getPropertyCount();
+        for (int i = 0; i < getPropertyCount(); i++) {
+            propertiesArray[i] = ownProperties.get(i);
+        }
+        return propertiesArray;
+    }
+
+    @Override
+    public final DynamicObject newInstance() {
+        return layout.newInstance(this);
+    }
+
+    @Override
+    public final DynamicObjectFactory createFactory() {
+        final List<Property> properties = getPropertyListInternal(true);
+        for (Iterator<Property> iterator = properties.iterator(); iterator.hasNext();) {
+            Property property = iterator.next();
+            // skip non-instance fields
+            assert property.getLocation() != layout.getPrimitiveArrayLocation();
+            if (property.getLocation() instanceof ValueLocation) {
+                iterator.remove();
+            }
+        }
+
+        return new DynamicObjectFactory() {
+            @CompilationFinal private final PropertyImpl[] instanceFields = properties.toArray(new PropertyImpl[properties.size()]);
+
+            @ExplodeLoop
+            public DynamicObject newInstance(Object... initialValues) {
+                DynamicObject store = ShapeImpl.this.newInstance();
+                for (int i = 0; i < instanceFields.length; i++) {
+                    instanceFields[i].setInternal(store, initialValues[i]);
+                }
+                return store;
+            }
+
+            public Shape getShape() {
+                return ShapeImpl.this;
+            }
+        };
+    }
+
+    @Override
+    public Object getMutex() {
+        return getRoot();
+    }
+
+    @Override
+    public Shape tryMerge(Shape other) {
+        return null;
+    }
+
+    public abstract static class BaseAllocator extends Allocator {
+        protected final LayoutImpl layout;
+        protected int objectArraySize;
+        protected int objectFieldSize;
+        protected int primitiveFieldSize;
+        protected int primitiveArraySize;
+        protected boolean hasPrimitiveArray;
+        protected int depth;
+
+        protected BaseAllocator(LayoutImpl layout) {
+            this.layout = layout;
+        }
+
+        protected BaseAllocator(ShapeImpl shape) {
+            this(shape.getLayout());
+            this.objectArraySize = shape.objectArraySize;
+            this.objectFieldSize = shape.objectFieldSize;
+            this.primitiveFieldSize = shape.primitiveFieldSize;
+            this.primitiveArraySize = shape.primitiveArraySize;
+            this.hasPrimitiveArray = shape.hasPrimitiveArray;
+            this.depth = shape.depth;
+        }
+
+        protected abstract Location moveLocation(Location oldLocation);
+
+        protected abstract Location newObjectLocation(boolean useFinal, boolean nonNull);
+
+        protected abstract Location newTypedObjectLocation(boolean useFinal, Class<?> type, boolean nonNull);
+
+        protected abstract Location newIntLocation(boolean useFinal);
+
+        protected abstract Location newDoubleLocation(boolean useFinal);
+
+        protected abstract Location newLongLocation(boolean useFinal);
+
+        protected abstract Location newBooleanLocation(boolean useFinal);
+
+        @Override
+        public final Location constantLocation(Object value) {
+            return new ConstantLocation(value);
+        }
+
+        @Override
+        protected Location locationForValue(Object value, boolean useFinal, boolean nonNull) {
+            if (value instanceof Integer) {
+                return newIntLocation(useFinal);
+            } else if (value instanceof Double) {
+                return newDoubleLocation(useFinal);
+            } else if (value instanceof Long) {
+                return newLongLocation(useFinal);
+            } else if (value instanceof Boolean) {
+                return newBooleanLocation(useFinal);
+            } else if (ObjectStorageOptions.TypedObjectLocations && value != null) {
+                return newTypedObjectLocation(useFinal, value.getClass(), nonNull);
+            }
+            return newObjectLocation(useFinal, nonNull && value != null);
+        }
+
+        protected abstract Location locationForValueUpcast(Object value, Location oldLocation);
+
+        @Override
+        protected Location locationForType(Class<?> type, boolean useFinal, boolean nonNull) {
+            if (type == int.class) {
+                return newIntLocation(useFinal);
+            } else if (type == double.class) {
+                return newDoubleLocation(useFinal);
+            } else if (type == long.class) {
+                return newLongLocation(useFinal);
+            } else if (type == boolean.class) {
+                return newBooleanLocation(useFinal);
+            } else if (ObjectStorageOptions.TypedObjectLocations && type != null && type != Object.class) {
+                assert !type.isPrimitive() : "unsupported primitive type";
+                return newTypedObjectLocation(useFinal, type, nonNull);
+            }
+            return newObjectLocation(useFinal, nonNull);
+        }
+
+        protected Location newDualLocation(Class<?> type) {
+            return new DualLocation((InternalLongLocation) newLongLocation(false), (ObjectLocation) newObjectLocation(false, false), layout, type);
+        }
+
+        protected DualLocation newDualLocationForValue(Object value) {
+            Class<?> initialType = null;
+            if (value instanceof Integer) {
+                initialType = int.class;
+            } else if (value instanceof Double) {
+                initialType = double.class;
+            } else if (value instanceof Boolean) {
+                initialType = boolean.class;
+            } else {
+                initialType = Object.class;
+            }
+            return new DualLocation((InternalLongLocation) newLongLocation(false), (ObjectLocation) newObjectLocation(false, false), layout, initialType);
+        }
+
+        protected Location newDeclaredDualLocation(Object value) {
+            return new DeclaredDualLocation((InternalLongLocation) newLongLocation(false), (ObjectLocation) newObjectLocation(false, false), value, layout);
+        }
+
+        protected <T extends Location> T advance(T location0) {
+            if (location0 instanceof LocationImpl) {
+                LocationImpl location = (LocationImpl) location0;
+                objectArraySize += location.objectArrayCount();
+                primitiveArraySize += location.primitiveArrayCount();
+                primitiveFieldSize += location.primitiveFieldCount();
+                if (layout.hasPrimitiveExtensionArray()) {
+                    hasPrimitiveArray |= location == layout.getPrimitiveArrayLocation() || location.primitiveArrayCount() != 0;
+                    objectFieldSize = location == layout.getPrimitiveArrayLocation() ? objectFieldSize : Math.max(objectFieldSize, layout.objectFieldIndex(location) + location.objectFieldCount());
+                } else {
+                    assert !hasPrimitiveArray && location.primitiveArrayCount() == 0;
+                    objectFieldSize += location.objectFieldCount();
+                }
+            }
+            depth++;
+            return location0;
+        }
+
+        @Override
+        public BaseAllocator addLocation(Location location) {
+            advance(location);
+            return this;
+        }
+    }
+
+    private static void debugRegisterShape(ShapeImpl newShape) {
+        if (ObjectStorageOptions.DumpShapes) {
+            Debug.registerShape(newShape);
+        }
+    }
+
+    /**
+     * Match all filter.
+     */
+    public static final Pred<Property> ALL = new Pred<Property>() {
+        public boolean test(Property t) {
+            return true;
+        }
+    };
+
+    private static final DebugCounter shapeCount = DebugCounter.create("Shapes allocated total");
+    private static final DebugCounter shapeCloneCount = DebugCounter.create("Shapes allocated cloned");
+    private static final DebugCounter shapeCacheHitCount = DebugCounter.create("Shape cache hits");
+    private static final DebugCounter shapeCacheMissCount = DebugCounter.create("Shape cache misses");
+
+    protected static final DebugCounter propertyListAllocCount = DebugCounter.create("Property lists allocated");
+    protected static final DebugCounter propertyListShareCount = DebugCounter.create("Property lists shared");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Transition.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013, 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.
+ *
+ * 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 com.oracle.truffle.object;
+
+import java.util.*;
+
+import com.oracle.truffle.api.object.*;
+
+public abstract class Transition {
+    @Override
+    public int hashCode() {
+        int result = 1;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        return true;
+    }
+
+    public abstract static class PropertyTransition extends Transition {
+        private final Property property;
+
+        public PropertyTransition(Property property) {
+            this.property = property;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + ((property == null) ? 0 : property.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!super.equals(obj)) {
+                return false;
+            }
+            PropertyTransition other = (PropertyTransition) obj;
+            if (!Objects.equals(property, other.property)) {
+                return false;
+            }
+            return true;
+        }
+
+        public Property getProperty() {
+            return property;
+        }
+    }
+
+    public static final class AddTransition extends PropertyTransition {
+        public AddTransition(Property property) {
+            super(property);
+        }
+    }
+
+    public static final class RemoveTransition extends PropertyTransition {
+        public RemoveTransition(Property property) {
+            super(property);
+        }
+    }
+
+    public static final class OperationsTransition extends Transition {
+        private final ObjectType operations;
+
+        public OperationsTransition(ObjectType operations) {
+            this.operations = operations;
+        }
+
+        public ObjectType getOperations() {
+            return operations;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            return super.equals(other) && Objects.equals(operations, ((OperationsTransition) other).operations);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = super.hashCode();
+            result = prime * result + ((operations == null) ? 0 : operations.hashCode());
+            return result;
+        }
+    }
+
+    public static final class TypeTransition extends PropertyTransition {
+        private final Property after;
+
+        public TypeTransition(Property before, Property after) {
+            super(before);
+            this.after = after;
+        }
+
+        public Property getPropertyBefore() {
+            return this.getProperty();
+        }
+
+        public Property getPropertyAfter() {
+            return after;
+        }
+    }
+
+    public static final class ReservePrimitiveArrayTransition extends Transition {
+        public ReservePrimitiveArrayTransition() {
+        }
+    }
+
+    public String getShortName() {
+        return this.getClass().getSimpleName().replaceFirst("Transition$", "").toLowerCase();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/GraphvizShapeVisitor.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object.debug;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+
+public class GraphvizShapeVisitor extends DebugShapeVisitor<GraphvizShapeVisitor> {
+    private final Set<Shape> drawn;
+    private final StringBuilder sb = new StringBuilder();
+
+    public GraphvizShapeVisitor() {
+        this.drawn = new HashSet<>();
+    }
+
+    @Override
+    public GraphvizShapeVisitor visitShape(Shape shape, Map<? extends Transition, ? extends Shape> transitions) {
+        if (!drawn.add(shape)) {
+            return null;
+        }
+
+        String prefix = "s";
+        sb.append(prefix).append(getId(shape));
+        sb.append(" [label=\"");
+        if (shape.getLastProperty() != null) {
+            sb.append(escapeString(shape.getLastProperty().toString()));
+        } else {
+            sb.append("ROOT");
+        }
+        sb.append("\"");
+        sb.append(", shape=\"rectangle\"");
+        if (!shape.isValid()) {
+            sb.append(", color=\"red\", style=dotted");
+        }
+        sb.append("];");
+
+        for (Entry<? extends Transition, ? extends Shape> entry : transitions.entrySet()) {
+            Shape dst = entry.getValue();
+            dst.accept(this);
+            assert drawn.contains(dst);
+
+            sb.append(prefix).append(getId(shape)).append("->").append(prefix).append(getId(dst));
+            sb.append(" [label=\"").append(escapeString(entry.getKey().getShortName())).append("\"]");
+            sb.append(";");
+        }
+
+        return this;
+    }
+
+    private static String escapeString(String str) {
+        return str.replaceAll("\\\\", "\\\\").replaceAll("\"", "\\\\\"");
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("digraph{").append(sb).append("}").toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/JSONShapeVisitor.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object.debug;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.api.utilities.JSONHelper.JSONArrayBuilder;
+import com.oracle.truffle.api.utilities.JSONHelper.JSONObjectBuilder;
+import com.oracle.truffle.object.*;
+import com.oracle.truffle.object.Transition.*;
+
+public class JSONShapeVisitor extends DebugShapeVisitor<JSONObjectBuilder> {
+    @Override
+    public JSONObjectBuilder visitShape(Shape shape, Map<? extends Transition, ? extends Shape> transitions) {
+        JSONObjectBuilder sb = JSONHelper.object();
+        JSONArrayBuilder transitionarray = JSONHelper.array();
+        for (Entry<? extends Transition, ? extends Shape> entry : transitions.entrySet()) {
+            transitionarray.add(JSONHelper.object().add("transition", dumpTransition(entry.getKey())).add("successor", getId(entry.getValue())));
+        }
+        JSONArrayBuilder propertiesarray = JSONHelper.array();
+        for (Property p : shape.getPropertyList()) {
+            propertiesarray.add(dumpProperty(p));
+        }
+        sb.add("id", getId(shape));
+        sb.add("properties", propertiesarray);
+        sb.add("transitions", transitionarray);
+        sb.add("predecessor", shape.getParent() != null ? getId(shape.getParent()) : null);
+        sb.add("valid", shape.isValid());
+        return sb;
+    }
+
+    public JSONObjectBuilder dumpProperty(Property property) {
+        return JSONHelper.object().add("id", property.getKey().toString()).add("location", dumpLocation(property.getLocation())).add("flags", property.getFlags());
+    }
+
+    public JSONObjectBuilder dumpTransition(Transition transition) {
+        JSONObjectBuilder sb = JSONHelper.object().add("type", transition.getShortName());
+        if (transition instanceof PropertyTransition) {
+            sb.add("property", dumpProperty(((PropertyTransition) transition).getProperty()));
+        }
+        return sb;
+    }
+
+    public JSONObjectBuilder dumpLocation(Location location) {
+        JSONObjectBuilder obj = JSONHelper.object();
+        obj.add("type", (location instanceof TypedLocation ? ((TypedLocation) location).getType() : Object.class).getName());
+        // if (location instanceof Locations.FieldLocation) {
+        // obj.add("offset", ((Locations.FieldLocation) location).getOffset());
+        // }
+        // if (location instanceof Locations.ArrayLocation) {
+        // obj.add("index", ((Locations.ArrayLocation) location).getIndex());
+        // }
+        if (location instanceof Locations.ValueLocation) {
+            obj.add("value", String.valueOf(((Locations.ValueLocation) location).get(null, false)));
+        }
+        return obj;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/ShapeProfiler.java	Tue Nov 18 23:19:43 2014 +0100
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2014, 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.
+ *
+ * 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 com.oracle.truffle.object.debug;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.oracle.truffle.api.object.*;
+import com.oracle.truffle.object.*;
+
+public class ShapeProfiler {
+    private static final String LINE_SEPARATOR = "***********************************************";
+    private static final String BULLET = "* ";
+    private static final String TOKEN_SEPARATOR = "\t";
+    private final ConcurrentLinkedQueue<DynamicObject> queue;
+
+    public ShapeProfiler() {
+        queue = new ConcurrentLinkedQueue<>();
+    }
+
+    public void track(DynamicObject obj) {
+        queue.add(obj);
+    }
+
+    public void dump(PrintWriter out) {
+        ShapeStats globalStats = new ShapeStats("Cumulative results for all shapes");
+        for (DynamicObject obj : queue) {
+            Shape shape = obj.getShape();
+            globalStats.profile(shape);
+        }
+
+        globalStats.dump(out);
+    }
+
+    public void dump(PrintWriter out, int topResults) {
+        if (topResults > 0) {
+            IdentityHashMap<Shape, ShapeStats> shapeMap = new IdentityHashMap<>();
+
+            for (DynamicObject obj : queue) {
+                Shape shape = obj.getShape();
+                ShapeStats stats = shapeMap.get(shape);
+                if (stats == null) {
+                    shapeMap.put(shape, stats = new ShapeStats(createLabel(shape)));
+                }
+                stats.profile(shape);
+            }
+
+            List<ShapeStats> allStats = new ArrayList<>(shapeMap.values());
+            Collections.sort(allStats, new Comparator<ShapeStats>() {
+                public int compare(ShapeStats a, ShapeStats b) {
+                    return Long.compare(b.jsObjects, a.jsObjects);
+                }
+            });
+
+            ShapeStats avgStats = new ShapeStats("Cumulative results for top " + topResults + " shapes");
+            for (int i = 0; i < topResults; i++) {
+                ShapeStats stats = allStats.get(i);
+                stats.setLabel("Shape " + (i + 1) + ": " + stats.getLabel());
+                stats.dump(out);
+                avgStats.add(stats);
+            }
+            avgStats.dump(out);
+        }
+        // Dump also cumulative results.
+        dump(out);
+    }
+
+    private static String createLabel(Shape shape) {
+        String label = shape.toString();
+        return label.substring(label.indexOf('{') + 1, label.lastIndexOf('}'));
+    }
+
+    private static class ShapeStats {
+        private String label;
+        private long jsObjects;
+        private long oac;
+        private long oas;
+        private long ofs;
+        private long pac;
+        private long pas;
+        private long pfs;
+
+        public ShapeStats(String label) {
+            this.label = label;
+        }
+
+        public String getLabel() {
+            return label;
+        }
+
+        public void setLabel(String label) {
+            this.label = label;
+        }
+
+        public void profile(Shape shape) {
+            jsObjects++;
+            oac += shape.getObjectArrayCapacity();
+            oas += shape.getObjectArraySize();
+            ofs += shape.getObjectFieldSize();
+            pac += shape.getPrimitiveArrayCapacity();
+            pas += shape.getPrimitiveArraySize();
+            pfs += shape.getPrimitiveFieldSize();
+        }
+
+        public void add(ShapeStats stats) {
+            jsObjects += stats.jsObjects;
+            oac += stats.oac;
+            oas += stats.oas;
+            ofs += stats.ofs;
+            oac += stats.oac;
+            oas += stats.oas;
+            ofs += stats.ofs;
+        }
+
+        public void dump(PrintWriter out) {
+            DecimalFormat format = new DecimalFormat("###.####");
+            out.println(LINE_SEPARATOR);
+            out.println(BULLET + label);
+            out.println(LINE_SEPARATOR);
+            out.println(BULLET + "Allocated objects:\t" + jsObjects);
+            out.println(BULLET + "Total object array capacity:\t" + oac);
+            out.println(BULLET + "Total object array size:\t" + oas);
+            out.println(BULLET + "Total object field size:\t" + ofs);
+            out.println(BULLET + "Average object array capacity:\t" + avgOAC(format));
+            out.println(BULLET + "Average object array size:\t" + avgOAS(format));
+            out.println(BULLET + "Average object field size:\t" + avgOFS(format));
+            out.println(LINE_SEPARATOR);
+            out.println(BULLET + "Total primitive array capacity:\t" + pac);
+            out.println(BULLET + "Total primitive array size:\t" + pas);
+            out.println(BULLET + "Total primitive field size:\t" + pfs);
+            out.println(BULLET + "Average primitive array capacity:\t" + avgPAC(format));
+            out.println(BULLET + "Average primitive array size:\t" + avgPAS(format));
+            out.println(BULLET + "Average primitive field size:\t" + avgPFS(format));
+            out.println(LINE_SEPARATOR);
+            out.println(BULLET + toString());
+            out.println(LINE_SEPARATOR + "\n");
+            out.flush();
+        }
+
+        @Override
+        public String toString() {
+            DecimalFormat format = new DecimalFormat("###.####");
+            // @formatter:off
+            return "{" + label + "}" + TOKEN_SEPARATOR
+                   + jsObjects + TOKEN_SEPARATOR
+                   + avgOAC(format) + TOKEN_SEPARATOR
+                   + avgOAS(format) + TOKEN_SEPARATOR
+                   + avgOFS(format) + TOKEN_SEPARATOR
+                   + avgPAC(format) + TOKEN_SEPARATOR
+                   + avgPAS(format) + TOKEN_SEPARATOR
+                   + avgPFS(format);
+            // @formatter:on
+        }
+
+        private String avgOAC(DecimalFormat format) {
+            return format.format((double) oac / jsObjects);
+        }
+
+        private String avgOAS(DecimalFormat format) {
+            return format.format((double) oas / jsObjects);
+        }
+
+        private String avgOFS(DecimalFormat format) {
+            return format.format((double) ofs / jsObjects);
+        }
+
+        private String avgPAC(DecimalFormat format) {
+            return format.format((double) pac / jsObjects);
+        }
+
+        private String avgPAS(DecimalFormat format) {
+            return format.format((double) pas / jsObjects);
+        }
+
+        private String avgPFS(DecimalFormat format) {
+            return format.format((double) pfs / jsObjects);
+        }
+    }
+
+    public static ShapeProfiler getInstance() {
+        return shapeProf;
+    }
+
+    private static final ShapeProfiler shapeProf;
+    static {
+        if (ObjectStorageOptions.Profile) {
+            shapeProf = new ShapeProfiler();
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                @Override
+                public void run() {
+                    getInstance().dump(new PrintWriter(System.out), ObjectStorageOptions.ProfileTopResults);
+                }
+            });
+        } else {
+            shapeProf = null;
+        }
+    }
+}
--- a/mx/suite.py	Tue Nov 18 16:18:45 2014 +0100
+++ b/mx/suite.py	Tue Nov 18 23:19:43 2014 +0100
@@ -1141,6 +1141,24 @@
       "workingSets" : "API,Truffle",
     },
 
+    "com.oracle.truffle.object" : {
+      "subDir" : "graal",
+      "sourceDirs" : ["src"],
+      "dependencies" : ["com.oracle.truffle.api.object"],
+      "checkstyle" : "com.oracle.graal.graph",
+      "javaCompliance" : "1.7",
+      "workingSets" : "Truffle",
+    },
+
+    "com.oracle.truffle.object.basic" : {
+      "subDir" : "graal",
+      "sourceDirs" : ["src"],
+      "dependencies" : ["com.oracle.truffle.object"],
+      "checkstyle" : "com.oracle.graal.graph",
+      "javaCompliance" : "1.7",
+      "workingSets" : "Truffle",
+    },
+
     "com.oracle.truffle.sl" : {
       "subDir" : "graal",
       "sourceDirs" : ["src"],