All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.object.DefaultLayout Maven / Gradle / Ivy

Go to download

Truffle is a multi-language framework for executing dynamic languages that achieves high performance when combined with Graal.

There is a newer version: 24.1.1
Show newest version
/*
 * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.object;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.graalvm.nativeimage.ImageInfo;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.object.Shape.Allocator;
import com.oracle.truffle.object.CoreLocations.LongLocation;
import com.oracle.truffle.object.CoreLocations.ObjectLocation;

import sun.misc.Unsafe;

@SuppressWarnings("deprecation")
class DefaultLayout extends LayoutImpl {
    private final ObjectLocation[] objectFields;
    private final LongLocation[] primitiveFields;
    private final CoreLocation objectArrayLocation;
    private final CoreLocation primitiveArrayLocation;

    static final ObjectLocation[] NO_OBJECT_FIELDS = new ObjectLocation[0];
    static final LongLocation[] NO_LONG_FIELDS = new LongLocation[0];

    private static final Map LAYOUT_MAP = new ConcurrentHashMap<>();

    DefaultLayout(Class dynamicObjectClass, LayoutStrategy strategy, int implicitCastFlags, ObjectLocation[] objectFields, LongLocation[] primitiveFields) {
        super(dynamicObjectClass, strategy, implicitCastFlags);
        this.primitiveArrayLocation = CoreLocations.PRIMITIVE_ARRAY_LOCATION;
        this.objectArrayLocation = CoreLocations.OBJECT_ARRAY_LOCATION;
        this.objectFields = objectFields;
        this.primitiveFields = primitiveFields;
    }

    DefaultLayout(Class dynamicObjectClass, LayoutStrategy strategy, int implicitCastFlags) {
        super(dynamicObjectClass, strategy, implicitCastFlags);
        this.primitiveArrayLocation = CoreLocations.PRIMITIVE_ARRAY_LOCATION;
        this.objectArrayLocation = CoreLocations.OBJECT_ARRAY_LOCATION;
        if (DynamicObject.class == dynamicObjectClass) {
            this.objectFields = NO_OBJECT_FIELDS;
            this.primitiveFields = NO_LONG_FIELDS;
        } else if (DynamicObject.class.isAssignableFrom(dynamicObjectClass)) {
            LayoutInfo layoutInfo = LayoutInfo.getOrCreateLayoutInfo(dynamicObjectClass);
            this.objectFields = layoutInfo.objectFields;
            this.primitiveFields = layoutInfo.primitiveFields;
        } else {
            throw new IllegalArgumentException(dynamicObjectClass.getName());
        }
    }

    public static LayoutImpl createCoreLayout(com.oracle.truffle.api.object.Layout.Builder builder) {
        Class type = getType(builder);
        EnumSet allowedImplicitCasts = getAllowedImplicitCasts(builder);
        int implicitCastFlags = implicitCastFlags(allowedImplicitCasts);
        return getOrCreateLayout(type, implicitCastFlags);
    }

    private static DefaultLayout getOrCreateLayout(Class type, int implicitCastFlags) {
        Key key = new Key(type, implicitCastFlags);
        DefaultLayout layout = LAYOUT_MAP.get(key);
        if (layout != null) {
            return layout;
        }
        DefaultLayout newLayout = new DefaultLayout(type, DefaultStrategy.SINGLETON, implicitCastFlags);
        layout = LAYOUT_MAP.putIfAbsent(key, newLayout);
        return layout == null ? newLayout : layout;
    }

    @Override
    public DynamicObject newInstance(Shape shape) {
        throw unsupported();
    }

    @Override
    protected DynamicObject construct(Shape shape) {
        throw unsupported();
    }

    @Override
    protected boolean isLegacyLayout() {
        return false;
    }

    static UnsupportedOperationException unsupported() {
        CompilerDirectives.transferToInterpreterAndInvalidate();
        throw new UnsupportedOperationException("not supported by this object layout");
    }

    @Override
    protected Shape newShape(Object objectType, Object sharedData, int flags, Assumption singleContextAssumption) {
        return new ShapeBasic(this, sharedData, objectType, flags, singleContextAssumption);
    }

    @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 CoreLocation getObjectArrayLocation() {
        return objectArrayLocation;
    }

    @Override
    protected CoreLocation getPrimitiveArrayLocation() {
        return primitiveArrayLocation;
    }

    protected ObjectLocation getObjectFieldLocation(int index) {
        return objectFields[index];
    }

    protected LongLocation getPrimitiveFieldLocation(int index) {
        return primitiveFields[index];
    }

    protected int getLongFieldSize() {
        return CoreLocations.LONG_FIELD_SLOT_SIZE;
    }

    @Override
    public Allocator createAllocator() {
        LayoutImpl layout = this;
        return getStrategy().createAllocator(layout);
    }

    private static final class Key {
        final Class type;
        final int implicitCastFlags;

        Key(Class type, int implicitCastFlags) {
            this.type = type;
            this.implicitCastFlags = implicitCastFlags;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + implicitCastFlags;
            result = prime * result + ((type == null) ? 0 : type.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (!(obj instanceof Key)) {
                return false;
            }
            Key other = (Key) obj;
            return this.type == other.type && this.implicitCastFlags == other.implicitCastFlags;
        }
    }

    private static final class LayoutInfo {
        final ObjectLocation[] objectFields;
        final LongLocation[] primitiveFields;

        private static final ConcurrentMap, LayoutInfo> LAYOUT_INFO_MAP = new ConcurrentHashMap<>();
        private static final Unsafe UNSAFE = CoreLocations.getUnsafe();

        static LayoutInfo getOrCreateLayoutInfo(Class dynamicObjectClass) {
            LayoutInfo layoutInfo = LAYOUT_INFO_MAP.get(dynamicObjectClass);
            if (layoutInfo != null) {
                return layoutInfo;
            }

            if (ImageInfo.inImageRuntimeCode()) {
                throw new IllegalStateException("Layout not initialized ahead-of-time: " + dynamicObjectClass);
            }

            return createLayoutInfo(dynamicObjectClass);
        }

        private static LayoutInfo createLayoutInfo(Class dynamicObjectClass) {
            Class subclass = dynamicObjectClass.asSubclass(DynamicObject.class);
            List objectFieldList = new ArrayList<>();
            List longFieldList = new ArrayList<>();
            Class superclass = collectFields(subclass, objectFieldList, longFieldList);

            if (objectFieldList.size() + longFieldList.size() > CoreLocations.MAX_DYNAMIC_FIELDS) {
                throw new IllegalArgumentException("Too many @DynamicField annotated fields.");
            }

            LayoutInfo newLayoutInfo;
            if (superclass != subclass) {
                // This class does not declare any dynamic fields; reuse info from superclass
                newLayoutInfo = getOrCreateLayoutInfo(superclass);
            } else {
                newLayoutInfo = new LayoutInfo(objectFieldList, longFieldList);
            }
            LayoutInfo layoutInfo = LAYOUT_INFO_MAP.putIfAbsent(dynamicObjectClass, newLayoutInfo);
            return layoutInfo == null ? newLayoutInfo : layoutInfo;
        }

        private LayoutInfo(List objectFieldList, List longFieldList) {
            this.objectFields = objectFieldList.toArray(NO_OBJECT_FIELDS);
            this.primitiveFields = longFieldList.toArray(NO_LONG_FIELDS);
        }

        /**
         * Collects dynamic fields in class hierarchy (from high to low).
         *
         * @return the class lowermost in the hierarchy declaring dynamic fields
         */
        private static Class collectFields(Class clazz, List objectFieldList, List primitiveFieldList) {
            if (clazz == DynamicObject.class) {
                return clazz;
            }

            Class layoutClass = collectFields(clazz.getSuperclass().asSubclass(DynamicObject.class), objectFieldList, primitiveFieldList);

            Class dynamicFieldAnnotation = ACCESS.getDynamicFieldAnnotation();
            boolean hasDynamicFields = false;
            for (Field field : clazz.getDeclaredFields()) {
                if (Modifier.isStatic(field.getModifiers()) || field.isSynthetic()) {
                    assert !field.isAnnotationPresent(dynamicFieldAnnotation);
                    continue;
                }

                if (field.getAnnotation(dynamicFieldAnnotation) != null) {
                    checkDynamicFieldType(field);
                    assert field.getDeclaringClass() == clazz;

                    hasDynamicFields = true;
                    if (field.getType() == Object.class) {
                        objectFieldList.add(new CoreLocations.DynamicObjectFieldLocation(objectFieldList.size(), field));
                    } else if (field.getType() == long.class) {
                        long offset = UNSAFE.objectFieldOffset(field);
                        if (offset % Long.BYTES == 0) {
                            primitiveFieldList.add(new CoreLocations.DynamicLongFieldLocation(primitiveFieldList.size(), offset, clazz));
                        }
                    }
                }
            }

            if (hasDynamicFields) {
                layoutClass = clazz;
            }
            return layoutClass;
        }

        private static void checkDynamicFieldType(Field field) {
            if (field.getType() != Object.class && field.getType() != int.class && field.getType() != long.class) {
                throw new IllegalArgumentException("@DynamicField annotated field type must be either Object or int or long: " + field);
            }
            if (Modifier.isFinal(field.getModifiers())) {
                throw new IllegalArgumentException("@DynamicField annotated field must not be final: " + field);
            }
        }

        @Override
        public String toString() {
            return "LayoutInfo [objectFields=" + Arrays.toString(objectFields) + ", primitiveFields=" + Arrays.toString(primitiveFields) + "]";
        }
    }

    /**
     * Resets the state for native image generation.
     */
    static void resetNativeImageState() {
        LAYOUT_MAP.clear();
        LayoutInfo.LAYOUT_INFO_MAP.clear();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy