/* * 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.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.object.DynamicObject; import com.oracle.truffle.api.object.LocationFactory; import com.oracle.truffle.api.object.Property; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.object.Locations.ValueLocation; /** @since 0.17 or earlier */ public abstract class DynamicObjectImpl extends DynamicObject implements Cloneable { private ShapeImpl shape; /** @since 0.17 or earlier */ public static final DebugCounter reshapeCount = DebugCounter.create("Reshape count"); /** @since 0.17 or earlier */ protected DynamicObjectImpl(Shape shape) { assert shape instanceof ShapeImpl; initialize(shape); setShape(shape); if (ObjectStorageOptions.Profile) { Debug.trackObject(this); } } /** @since 0.17 or earlier */ @SuppressWarnings("deprecation") public Object getTypeIdentifier() { return getShape(); } /** @since 0.17 or earlier */ @Override public ShapeImpl getShape() { return shape; } /** @since 0.17 or earlier */ protected void setShape(Shape shape) { assert shape.getLayout().getType().isInstance(this); this.shape = (ShapeImpl) shape; } /** @since 0.17 or earlier */ protected abstract void initialize(Shape initialShape); /** @since 0.17 or earlier */ public final void setShapeAndResize(Shape newShape) { setShapeAndResize(getShape(), newShape); } /** @since 0.17 or earlier */ @Override public final void setShapeAndResize(Shape oldShape, Shape newShape) { assert getShape() == oldShape : "wrong old shape"; assert !oldShape.isShared(); if (oldShape != newShape) { resizeStore(oldShape, newShape); setShape(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) * @since 0.17 or earlier */ @Override public final void setShapeAndGrow(Shape oldShape, Shape newShape) { assert getShape() == oldShape : "wrong old shape"; if (oldShape != newShape) { assert checkSetShape(oldShape, newShape); growStore(oldShape, newShape); setShape(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); } } /** @since 0.17 or earlier */ protected abstract void growObjectStore(Shape oldShape, Shape newShape); /** @since 0.17 or earlier */ 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); } } /** @since 0.17 or earlier */ protected abstract void resizePrimitiveStore(Shape oldShape, Shape newShape); /** @since 0.17 or earlier */ 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; return true; } /** * Check whether the extension arrays are in accordance with the description in the shape. * * @since 0.17 or earlier */ protected abstract boolean checkExtensionArrayInvariants(Shape newShape); /** @since 0.17 or earlier */ @Override protected final DynamicObject clone() { try { return (DynamicObject) super.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException(); } } /** @since 0.17 or earlier */ protected abstract DynamicObject cloneWithShape(Shape currentShape); /** @since 0.17 or earlier */ protected abstract void reshape(ShapeImpl newShape); /** @since 0.17 or earlier */ public final void copyProperties(DynamicObject fromObject, Shape ancestor) { ShapeImpl fromShape = (ShapeImpl) fromObject.getShape(); ShapeImpl toShape = getShape(); assert toShape.isRelated(ancestor); assert toShape.isValid(); assert ancestor.isValid(); assert !fromShape.isShared(); PropertyMap ancestorMap = ((ShapeImpl) ancestor).getPropertyMap(); PropertyMap fromMap = fromShape.getPropertyMap(); for (PropertyMap toMap = toShape.getPropertyMap(); !toMap.isEmpty() && toMap != ancestorMap; toMap = toMap.getParentMap()) { Property toProperty = toMap.getLastProperty(); Property fromProperty = fromMap.get(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 (fromProperty == fromMap.getLastProperty()) { // no property is looked up twice, so we can skip over to parent fromMap = fromMap.getParentMap(); } } } /** @since 0.17 or earlier */ @TruffleBoundary public boolean changeFlags(Object key, int newFlags) { Shape oldShape = getShape(); Property existing = oldShape.getProperty(key); 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; } } /** @since 0.17 or earlier */ public String debugDump(int level) { return debugDump(0, level); } /** @since 0.17 or earlier */ public String debugDump(int level, int levelStop) { return Debug.dumpObject(this, level, levelStop); } /** @since 0.17 or earlier */ @Override public String toString() { return getShape().getObjectType().toString(this); } /** @since 0.17 or earlier */ @Override public boolean equals(Object obj) { return getShape().getObjectType().equals(this, obj); } /** @since 0.17 or earlier */ @Override public int hashCode() { return getShape().getObjectType().hashCode(this); } /** @since 0.17 or earlier */ @Override @TruffleBoundary public Object get(Object key, Object defaultValue) { Property existing = getShape().getProperty(key); if (existing != null) { return existing.get(this, false); } else { return defaultValue; } } /** @since 0.17 or earlier */ @Override @TruffleBoundary public boolean set(Object key, Object value) { Property existing = getShape().getProperty(key); if (existing != null) { existing.setGeneric(this, value, null); return true; } else { return false; } } /** @since 0.17 or earlier */ @Override @TruffleBoundary public void define(Object key, Object value, int flags) { define(key, value, flags, getShape().getLayout().getStrategy().getDefaultLocationFactory()); } /** @since 0.17 or earlier */ @Override @TruffleBoundary public void define(Object key, Object value, int flags, LocationFactory locationFactory) { ShapeImpl oldShape = getShape(); oldShape.getLayout().getStrategy().objectDefineProperty(this, key, value, flags, locationFactory, oldShape); } /** @since 0.17 or earlier */ @Override @TruffleBoundary public boolean delete(Object key) { ShapeImpl oldShape = getShape(); Property existing = oldShape.getProperty(key); if (existing != null) { oldShape.getLayout().getStrategy().objectRemoveProperty(this, existing, oldShape); return true; } else { return false; } } /** @since 0.17 or earlier */ @Override public int size() { return getShape().getPropertyCount(); } /** @since 0.17 or earlier */ @Override public boolean isEmpty() { return size() == 0; } /** @since 0.17 or earlier */ @Override public final boolean updateShape() { return getShape().getLayout().getStrategy().updateShape(this); } /** @since 0.17 or earlier */ @Override public final DynamicObject copy(Shape currentShape) { return cloneWithShape(currentShape); } /** @since 0.17 or earlier */ @Override public ForeignAccess getForeignAccess() { return getShape().getForeignAccessFactory(this); } }