# HG changeset patch # User Andreas Woess # Date 1416349183 -3600 # Node ID 2c3666f44855e578fc4c3e764dc6273af9d49fb8 # Parent f439fdb137a3c968c6aa37e993ad3e476a89e30a Truffle: initial commit of object API implementation diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/META-INF/services/com.oracle.truffle.api.object.LayoutFactory --- /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 diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicAllocator.java --- /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); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicLayout.java --- /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 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 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; + } + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/BasicLocations.java --- /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 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 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 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 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 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 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 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 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 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 getType() { + return boolean.class; + } + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultLayoutFactory.java --- /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; + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DefaultStrategy.java --- /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); + } + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/DynamicObjectBasic.java --- /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; + } + }; + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object.basic/src/com/oracle/truffle/object/basic/ShapeBasic.java --- /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); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Debug.java --- /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 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); + } + } + })); + } + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/DebugShapeVisitor.java --- /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 implements ShapeVisitor { + public R visitShape(Shape shape) { + return visitShape(shape, Collections.unmodifiableMap(((ShapeImpl) shape).getTransitionMapForRead())); + } + + public abstract R visitShape(Shape shape, Map transitions); + + public static String getId(Shape shape) { + return Integer.toHexString(shape.hashCode()); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/DynamicObjectImpl.java --- /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 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); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutImpl.java --- /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 clazz; + private final int allowedImplicitCasts; + + protected LayoutImpl(EnumSet allowedImplicitCasts, Class 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 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 FinalLocation => FinalLocation + // 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; + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LayoutStrategy.java --- /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); +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/LocationImpl.java --- /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 toNonFinalLocation(); + } + + public interface TypedObjectLocation 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)); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Locations.java --- /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"; + } + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/ObjectStorageOptions.java --- /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"); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyImpl.java --- /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; + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/PropertyMap.java --- /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 { + 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 entry : reverseOrderEntrySet()) { + if (entry.getKey().equals(key)) { + return true; + } + } + return false; + } + + public boolean containsValue(Object value) { + for (Map.Entry entry : reverseOrderEntrySet()) { + if (entry.getValue().equals(value)) { + return true; + } + } + return false; + } + + public Property get(Object key) { + for (Map.Entry 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 m) { + throw unmodifiableException(); + } + + public void clear() { + throw unmodifiableException(); + } + + public Set keySet() { + return new AbstractSet() { + @Override + public Iterator iterator() { + Object[] keys = new Object[size()]; + Iterator> 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 values() { + return new AbstractSet() { + @Override + public Iterator iterator() { + Property[] values = new Property[size()]; + Iterator> 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> entrySet() { + return new AbstractSet>() { + @Override + public Iterator> iterator() { + @SuppressWarnings("unchecked") + Map.Entry[] entries = (Map.Entry[]) new Map.Entry[size()]; + Iterator> 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> reverseOrderEntrySet() { + return new AbstractSet>() { + @Override + public Iterator> iterator() { + return new Iterator>() { + PropertyMap current = PropertyMap.this; + + public Entry 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 reverseOrderKeys() { + return new AbstractSet() { + @Override + public Iterator iterator() { + return new Iterator() { + 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 reverseOrderValues() { + return new AbstractSet() { + @Override + public Iterator iterator() { + return new Iterator() { + 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 { + 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 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 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; + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/ShapeImpl.java --- /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 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 getTransitionMapForRead() { + return transitionMap != null ? transitionMap : Collections. emptyMap(); + } + + private Map 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 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 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 getPropertyList(Pred filter) { + LinkedList 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 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 getPropertyList() { + return getPropertyList(ALL); + } + + /** + * Returns all (also hidden) Property objects in this shape. + * + * @param ascending desired order + */ + @TruffleBoundary + @Override + public final List getPropertyListInternal(boolean ascending) { + LinkedList 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 getKeyList(Pred filter) { + LinkedList 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 getKeyList() { + return getKeyList(ALL); + } + + @Override + public Iterable 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 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 properties = new ArrayList<>(); + ShapeImpl current = this; + while (current != shape) { + properties.add(current.getLastProperty()); + current = current.parent; + } + ShapeImpl newShape = shape.parent; + for (ListIterator 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 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 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 properties = this.getPropertyListInternal(true); + ShapeImpl newShape = ((ShapeImpl) destination).addPropertiesInternal(properties); + return newShape; + } + + private ShapeImpl addPropertiesInternal(List 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 diff(Shape oldShape, Shape newShape) { + List oldList = oldShape.getPropertyListInternal(false); + List newList = newShape.getPropertyListInternal(false); + + List diff = new ArrayList<>(oldList); + diff.addAll(newList); + List 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 getProperties() { + if (getPropertyCount() != 0 && propertyArray == null) { + CompilerDirectives.transferToInterpreter(); + propertyArray = createPropertiesArray(); + } + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + 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 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 properties = getPropertyListInternal(true); + for (Iterator 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 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 ALL = new Pred() { + 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"); +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/Transition.java --- /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(); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/GraphvizShapeVisitor.java --- /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 { + private final Set drawn; + private final StringBuilder sb = new StringBuilder(); + + public GraphvizShapeVisitor() { + this.drawn = new HashSet<>(); + } + + @Override + public GraphvizShapeVisitor visitShape(Shape shape, Map 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 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(); + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/JSONShapeVisitor.java --- /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 { + @Override + public JSONObjectBuilder visitShape(Shape shape, Map transitions) { + JSONObjectBuilder sb = JSONHelper.object(); + JSONArrayBuilder transitionarray = JSONHelper.array(); + for (Entry 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; + } +} diff -r f439fdb137a3 -r 2c3666f44855 graal/com.oracle.truffle.object/src/com/oracle/truffle/object/debug/ShapeProfiler.java --- /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 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 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 allStats = new ArrayList<>(shapeMap.values()); + Collections.sort(allStats, new Comparator() { + 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; + } + } +} diff -r f439fdb137a3 -r 2c3666f44855 mx/suite.py --- 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"],